+ +
BSPMapGen
+ + + + source code + + +HaxeFlixel port of Timothy Hely's "Using-BSP-Trees-to-Generate-Game-Maps".
+ + ++ + + +
diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/11ty-bundle.js b/11ty-bundle.js new file mode 100644 index 000000000..5171d544f --- /dev/null +++ b/11ty-bundle.js @@ -0,0 +1,435 @@ + +/* ======================================================================== + * Bootstrap: dropdown.js v3.0.0 + * http://twbs.github.com/bootstrap/javascript.html#dropdowns + * ======================================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== */ + + ++function ($) +{ + "use strict"; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle=dropdown]' + var Dropdown = function (element) + { + var $el = $ (element).on ('click.bs.dropdown', this.toggle) + } + + Dropdown.prototype.toggle = function (e) + { + var $this = $ (this) + + if ($this.is ('.disabled, :disabled')) return + + var $parent = getParent ($this) + var isActive = $parent.hasClass ('open') + + clearMenus () + + if (!isActive) + { + if ('ontouchstart' in document.documentElement && !$parent.closest ('.navbar-nav').length) + { + // if mobile we we use a backdrop because click events don't delegate + $ ('
').insertAfter ($ (this)).on ('click', clearMenus) + } + + $parent.trigger (e = $.Event ('show.bs.dropdown')) + + if (e.isDefaultPrevented ()) return + + $parent + .toggleClass ('open') + .trigger ('shown.bs.dropdown') + + $this.focus () + } + + return false + } + + Dropdown.prototype.keydown = function (e) + { + if (!/(38|40|27)/.test (e.keyCode)) return + + var $this = $ (this) + + e.preventDefault () + e.stopPropagation () + + if ($this.is ('.disabled, :disabled')) return + + var $parent = getParent ($this) + var isActive = $parent.hasClass ('open') + + if (!isActive || (isActive && e.keyCode == 27)) + { + if (e.which == 27) $parent.find (toggle).focus () + return $this.click () + } + + var $items = $ ('[role=menu] li:not(.divider):visible a', $parent) + + if (!$items.length) return + + var index = $items.index ($items.filter (':focus')) + + if (e.keyCode == 38 && index > 0) index-- // up + if (e.keyCode == 40 && index < $items.length - 1) index++ // down + if (!~index) index = 0 + + $items.eq (index).focus () + } + + function clearMenus () + { + $ (backdrop).remove () + $ (toggle).each (function (e) + { + var $parent = getParent ($ (this)) + if (!$parent.hasClass ('open')) return + $parent.trigger (e = $.Event ('hide.bs.dropdown')) + if (e.isDefaultPrevented ()) return + $parent.removeClass ('open').trigger ('hidden.bs.dropdown') + }) + } + + function getParent ($this) + { + var selector = $this.attr ('data-target') + + if (!selector) + { + selector = $this.attr ('href') + selector = selector && /#/.test (selector) && selector.replace (/.*(?=#[^\s]*$)/, '') //strip for ie7 + } + + var $parent = selector && $ (selector) + + return $parent && $parent.length ? $parent : $this.parent () + } + + + // DROPDOWN PLUGIN DEFINITION + // ========================== + + var old = $.fn.dropdown + + $.fn.dropdown = function (option) + { + return this.each (function () + { + var $this = $ (this) + var data = $this.data ('dropdown') + + if (!data) $this.data ('dropdown', (data = new Dropdown (this))) + if (typeof option == 'string') data[option].call ($this) + }) + } + + $.fn.dropdown.Constructor = Dropdown + + + // DROPDOWN NO CONFLICT + // ==================== + + $.fn.dropdown.noConflict = function () + { + $.fn.dropdown = old + return this + } + + + // APPLY TO STANDARD DROPDOWN ELEMENTS + // =================================== + + $ (document) + .on ('click.bs.dropdown.data-api', clearMenus) + .on ('click.bs.dropdown.data-api', '.dropdown form', function (e) + { + e.stopPropagation () + }) + .on ('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) + .on ('keydown.bs.dropdown.data-api', toggle + ', [role=menu]', Dropdown.prototype.keydown) + +} (window.jQuery); + +/* ======================================================================== + * Bootstrap: transition.js v3.0.0 + * http://twbs.github.com/bootstrap/javascript.html#transitions + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== */ + + ++function ($) +{ + "use strict"; + + // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) + // ============================================================ + + function transitionEnd () + { + var el = document.createElement ('bootstrap') + + var transEndEventNames = { + 'WebkitTransition': 'webkitTransitionEnd', 'MozTransition': 'transitionend', 'OTransition': 'oTransitionEnd otransitionend', 'transition': 'transitionend' + } + + for (var name in transEndEventNames) + { + if (el.style[name] !== undefined) + { + return { end: transEndEventNames[name] } + } + } + } + + // http://blog.alexmaccaw.com/css-transitions + $.fn.emulateTransitionEnd = function (duration) + { + var called = false, $el = this + $ (this).one ($.support.transition.end, function () + { + called = true + }) + var callback = function () + { + if (!called) $ ($el).trigger ($.support.transition.end) + } + setTimeout (callback, duration) + return this + } + + $ (function () + { + $.support.transition = transitionEnd () + }) + +} (window.jQuery); + +/* ======================================================================== + * Bootstrap: collapse.js v3.0.0 + * http://twbs.github.com/bootstrap/javascript.html#collapse + * ======================================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== */ + + ++function ($) +{ + "use strict"; + + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ + + var Collapse = function (element, options) + { + this.$element = $ (element) + this.options = $.extend ({}, Collapse.DEFAULTS, options) + this.transitioning = null + + if (this.options.parent) this.$parent = $ (this.options.parent) + if (this.options.toggle) this.toggle () + } + + Collapse.DEFAULTS = { + toggle: true + } + + Collapse.prototype.dimension = function () + { + var hasWidth = this.$element.hasClass ('width') + return hasWidth ? 'width' : 'height' + } + + Collapse.prototype.show = function () + { + if (this.transitioning || this.$element.hasClass ('in')) return + + var startEvent = $.Event ('show.bs.collapse') + this.$element.trigger (startEvent) + if (startEvent.isDefaultPrevented ()) return + + var actives = this.$parent && this.$parent.find ('> .panel > .in') + + if (actives && actives.length) + { + var hasData = actives.data ('bs.collapse') + if (hasData && hasData.transitioning) return + actives.collapse ('hide') + hasData || actives.data ('bs.collapse', null) + } + + var dimension = this.dimension () + + this.$element + .removeClass ('collapse') + .addClass ('collapsing') + [dimension] (0) + + this.transitioning = 1 + + var complete = function () + { + this.$element + .removeClass ('collapsing') + .addClass ('in') + [dimension] ('auto') + this.transitioning = 0 + this.$element.trigger ('shown.bs.collapse') + } + + if (!$.support.transition) return complete.call (this) + + var scrollSize = $.camelCase (['scroll', dimension].join ('-')) + + this.$element + .one ($.support.transition.end, $.proxy (complete, this)) + .emulateTransitionEnd (350) + [dimension] (this.$element[0][scrollSize]) + } + + Collapse.prototype.hide = function () + { + if (this.transitioning || !this.$element.hasClass ('in')) return + + var startEvent = $.Event ('hide.bs.collapse') + this.$element.trigger (startEvent) + if (startEvent.isDefaultPrevented ()) return + + var dimension = this.dimension () + + this.$element + [dimension] (this.$element[dimension] ()) + [0].offsetHeight + + this.$element + .addClass ('collapsing') + .removeClass ('collapse') + .removeClass ('in') + + this.transitioning = 1 + + var complete = function () + { + this.transitioning = 0 + this.$element + .trigger ('hidden.bs.collapse') + .removeClass ('collapsing') + .addClass ('collapse') + } + + if (!$.support.transition) return complete.call (this) + + this.$element + [dimension] (0) + .one ($.support.transition.end, $.proxy (complete, this)) + .emulateTransitionEnd (350) + } + + Collapse.prototype.toggle = function () + { + this[this.$element.hasClass ('in') ? 'hide' : 'show'] () + } + + + // COLLAPSE PLUGIN DEFINITION + // ========================== + + var old = $.fn.collapse + + $.fn.collapse = function (option) + { + return this.each (function () + { + var $this = $ (this) + var data = $this.data ('bs.collapse') + var options = $.extend ({}, Collapse.DEFAULTS, $this.data (), typeof option == 'object' && option) + + if (!data) $this.data ('bs.collapse', (data = new Collapse (this, options))) + if (typeof option == 'string') data[option] () + }) + } + + $.fn.collapse.Constructor = Collapse + + + // COLLAPSE NO CONFLICT + // ==================== + + $.fn.collapse.noConflict = function () + { + $.fn.collapse = old + return this + } + + + // COLLAPSE DATA-API + // ================= + + $ (document).on ('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) + { + var $this = $ (this), href + var target = $this.attr ('data-target') + || e.preventDefault () + || (href = $this.attr ('href')) && href.replace (/.*(?=#[^\s]+$)/, '') //strip for ie7 + var $target = $ (target) + var data = $target.data ('bs.collapse') + var option = data ? 'toggle' : $this.data () + var parent = $this.attr ('data-parent') + var $parent = parent && $ (parent) + + if (!data || !data.transitioning) + { + if ($parent) $parent.find ('[data-toggle=collapse][data-parent="' + parent + '"]').not ($this).addClass ('collapsed') + $this[$target.hasClass ('in') ? 'addClass' : 'removeClass'] ('collapsed') + } + + $target.collapse (option) + }) + +} (window.jQuery); diff --git a/404.html b/404.html new file mode 100644 index 000000000..22f5f5176 --- /dev/null +++ b/404.html @@ -0,0 +1,171 @@ + + + + + + + + + +July 30, 2014 + +
The HaxeFlixel project and it's active community of game dev enthusiasts, have opened this +blog up to post about the latest updates, news and anything awesome happening related to +making games with HaxeFlixel and the projects around it.
+Although most of the HaxeFlixel activity happens on Github, Slack chat (ask for an invite) +and the @haxeflixel twitter and our google groups, this is resource can help keep users +up to date on the most important updates and changes to the codebase and community.
+Like everything HaxeFlixel, this blog is open for contributions from github by editing the +markdown files, just like our documentation.
+Feel free to fork and improve these blog posts and the website itself.
+So if you have ideas or requests shoot them at us ;)
+ + +July 31, 2014 + +
Hi, gamedevsam here, I'm a developer and evangelist of HaxeFlixel.
+The following blog post was written by Beeblerox (the original creator of HaxeFlixel) and was originally posted on his blog as a two part article (part 1 and part 2). In it, he goes into detail on how HaxeFlixel's rendering system is built on top of OpenFL's Tilesheet API.
+I want to tell you about tilesheet rendering in flixel, which is used on native targets by default (since some of you might me intereseted in it).
+But let’s start from Tilesheet API and how you work with it (Feel free to skip this part if you know it already).
+Tilesheet class allows you to draw multiple regions of image in one drawcall reasonably fast. These regions can be transformed (rotated, scaled, skewed) and tinted, plus they can have several blend modes (the most usefull is addition mode, which can be used for effects like fire and smoke).
+Let’s assume that you have some image you want to draw - “assets/tiles.png”.
+We are starting from instantiation of Tilesheet object:
+tilesheet = new Tilesheet(Assets.getBitmap(“assets/tiles.png”));
+
+Then we should define region of image (or tile) we want to draw. This is achieved with addTileRect() method, which takes 2 arguments:
+the first one is a Rectangle object - it is actual region of image to draw
+and the second one is a Pont object, which define “center” point of a tile. Added tile will be rotated around this point, if we apply rotation transformation to it. This point should be inside of a tile (or it will be bounded), so if you add tile with the size 32x32 pixels and the center at (50; 16), then actual center point will be at (32; 16).
+Center point argument is optional, and if you omit it then tile will be rotated around it’s middle point.
+// adding tiles to the tilesheet
+tileID1 = tilesheet.addTileRect(newRectangle(0, 0, 32, 32), new Point(16, 16));
+tileID2 = tilesheet.addTileRect(newRectangle(32, 0, 32, 32), new Point(16, 16));
+
+Then we should have some Graphics object to draw our tiles on.
+sprite = new Sprite();
+Lib.current.stage.addChild(sprite);
+graphicsToUse = sprite.graphics;
+
+To draw the tiles on screen we need three things:
+Graphics to draw then on
+Draw flag which tells to the program what tile transformations we want to use on our tiles in this drawcall.
+The simplest case is no transformation (we just draw rectangular region of image on specified position):
+drawFlag = 0;
+
+You can add tinting:
+drawFlag |= Tilesheet.TILE_RGB;
+
+We can add TILE_ALPHA flag to be able to change tile’s alpha:
+drawFlag |= Tilesheet.TILE_ALPHA;
+
+There are also TILE_ROTATION
and TILE_SCALE
(for uniform tile scaling) constants for “simple” transformations of a tile. But if you want to achieve some more complex transformation (like non-uniform scaling or skewing) then you have TILE_TRANS_2x2
constant.
And finally there are TILE_BLEND_ADD
constant for addition blending and TILE_SMOOTH
for smoothing scaled up graphics.
The amount of data for each tile depends on drawFlags value:
+data = [x1, y1, tileID1, x2, y2, tileID2];
+
+data = [x1, y1, tileID1, scale1, angle1, x2, y1, tileID2, scale2, angle2];
+
+data = [x1, y1, tileID1, scale1, angle1, red1, green1, blue1, alpha1, x2, y1, tileID2, scale2, angle2, red2, green2, blue2, alpha2];
+
+where red, green, blue and alpha are values between 0 and 1 (result color of each pixel will be product of these coefficients and original pixel colors).
+TILE_TRANS_2x2
:data = [x, y, tileID1, a, b, c, d, red, green, blue, alpha];
+
+where (a, b, c, d) are the transfromation matrix coefficients. You can get their values in two ways:
+a) by using Matrix class. For example, if you want to have non-uniform scale and rotation for your tile, then you can get it this way:
+matrix.identity();
+matrix.scale(scaleX, scaleY);
+matrix.rotate(angle);
+data = [x, y, tileID1, matrix.a, matrix.b, matrix.c, matrix.d, red, green, blue, alpha];
+
+b) or manually if you want to make some optimizations. That’s why drawing methods in flixels are so bloated - i just wanted to except some redundant calculations from it.
+So finally you can draw your tiles on the screen:
+tilesheet.drawTiles(graphicsToUse, data, false, drawFlags);
+
+If you want to see some working example, then i recommend you to look at Tiles sample project in nme library: https://github.com/haxenme/nme/tree/master/samples/20-Tiles
+Now when we know everything we need about Tilesheet class, it’s time to talk about flixel renderer a bit.
+As you remember we need Graphics object to render our tiles. FlxCamera objects contain flashSprite:Sprite variable inside which we have canvas:Sprite which graphics we use for rendering on the camera. We need canvas spite to be nested inside flashSprite for easy camera rotations. So if you don’t plan to implement such feature then you might use just one sprite without nesting.
+We also need data to render and render flags, which reflect what types of transformations (like rotation and tinting) apply to rendered tiles. This information is gathered every render cycle: we iterate through each sprite we have in our game. But to keep drawCalls as low as possible we need some sort of batching, which tries to draw everything with the same graphics in one draw call, and when we change graphics it breaks the batch and starts another. This functionality is split between FlxCamera. FlxSprite class and subclasses and DrawStackItem helper class.
+DrawStackItem objects store information about current batch: Tilesheet object to use for rendering, draw data array, information about draw flags (do we need to tint our tiles in the batch, or use blending), and the link to next DrawStackItem object (DrawStackItems are organized into linked list). Each camera have _headOfDrawStack variable which is head of DrawStackItems linked list.
+FlxSprite draw() method does the following:
+it gets DrawStackItem object to use from current FlxCamera. The result of this operation depends on sprite’s graphics, color and blend mode. So if one of these factors isn’t equal to the properties of current DrawStackItem, then current DrawStackItem will be “finalized” (breaks current batch) and new/empty DrawStackItem will be returned to sprite.
+sprite will add info about itself in current DrawStackItem (positio, tileID, transformation matrix coefficients, tinting and alpha).
+After we iterate through all sprites in our game state, game engine start actual render process.
+It iterate through each camera, clear graphics of cameras, fill them with background color (with graphics.drawRect() method), and then each camera iterate through its list of DrawStackItems. This iteration stage takes DrawStackItem’s tilesheet, draw data, draw flag and render it with drawTiles() method on camera’s canvas.graphics.
+That is how tilesheet rendering works in flixel. Feel free to ask me questions about it.
+You can reach Beeblerox on Twitter @teormech.
+ + +May 20, 2015 + +
Hello HaxeFlixel enthusiast, gamedevsam here!
+I have some big news to share with the HaxeFlixel community: HaxeFlixel is now on Patreon.
+Patreon is a crowd-funding platform that collects monthly donations to support artists and projects that people love. HaxeFlixel has joined Patreon to collect funds in order to reward contributions to the project, and give back to other open source projects that we think are awesome and deserve our support.
+Asking the community to give us money wasn't an easy decision. HaxeFlixel is a free, open source project, and every single one of our developers loves working on the engine, no one expects to get paid. We don't want our users to think we might some day charge for features or platforms. Every aspect of HaxeFlixel is free, and will always be free, that's the nature of open source projects.
+The main reason we are now accepting donations is because some members of our community asked us if they could give us money instead of contributing code to the engine (maybe because they've had success with the engine and want to give back, or maybe they don't have time to contribute code). For some time I was worried that dealing with money could lead to tensions within the community, but after thinking long and hard about it, I realized that this could be an opportunity to attract new talent to the project, an opportunity to help other open source projects that are also seeking funding, and an opportunity to reward long term members of this community that made all of this possible in the first place.
+Dealing with distribution of the funds will likely involve some trial and error. Most well funded open source projects rely on a company that charges for support. We don't have that luxury, so I had to come up with a fair way to distribute the funds.
+Here's how the money will be distributed:
+The first two points are pretty straight forward. Since I live in the US, I have to pay taxes on any money I receive in exchange for a service (in my case 25%). Another 25% will be sent to Chris (impaler) to cover the expense he incurs from hosting HaxeFlixel.com website. This might seem high to some of you, but keep in mind Chris has been hosting the website out of his own pocket for the last 2 years. This percentage might change with time (depending on how much money is raised, or if Chris wishes to receive a lesser amount).
+The final point is a bit vague, and that is on purpose. Instead of me deciding what to do with all the money, I'd like the community to have the power to decide who should get money and for what reason. If you think HaxeFlixel should reward someone monetarily for a contribution he/she made to the project, or if you know of a great open source project that could use our funding, send me an email with the subject line HaxeFlixel Funding Request, and include a description or a link to a web page that explains why this person or project deserves funding. If you think a person deserves funding for a contribution he/she made, don't forget to include their contact, or PayPal information in your request.
+At this point I'm the one responsible for making decisions how the money will be spent, and I will have the final say on whether to distribute the funds (and how much). Obviously if the person making the request is a long time supporter of the project, their request will be more likely to be fulfilled.
+As a final note, anyone within the owners team has access to the Patreon account, so if I don't do this properly, or something happens to me, someone else can easily take over this role.
+If you have any questions or concerns please get in touch with me via my website's contact form.
+Thanks for using HaxeFlixel, and please consider supporting the project on Patreon!
+ + +February 16, 2016 + +
We are proud to announce the release of HaxeFlixel 4.0.0! This is without a doubt the biggest release yet, with nearly 2000 new commits on the core repository alone since the last release.
+The highlights of this release are:
+For a more in-depth breakdown of the changes, have a look at our changelog. If you are mostly interested in the breaking changes to upgrade a project using HaxeFlixel 3.3.x, please refer to the upgrade guide.
+We would like to thank all contributors who helped with this release in any way, as well as our Patreon supporters. Check out the Financial Summary 06/15 - 02/16 blog post on Patreon if you're wondering what we do with your donations.
+Going forward, there are two things we want to focus on:
+The HaxeFlixel team
+ + +July 10, 2016 + +
HaxeFlixel 4.1.0 has just been released to Haxelib (as well as new versions of flixel-addons, ui and demos)! The full changelog contains a long list of minor improvements and bugfixes.
+Unlike previous minor releases, this release is intended to be used as a drop-in-replacement - there are no significant breaking changes. If you do encounter any issues, please let us know.
+The most exciting addition is probably the ability use GLSL shaders on single sprites and tilemaps (only supported with OpenFL Next and on native targets however). You can test this feature by compiling the FlxBunnyMark
demo to Cpp or Neko with -Dnext
:
New major versions of OpenFL and Lime have been released yesterday. Installing Flixel 4.1.0 will also install OpenFL 3.6.1 and Lime 2.9.1 because we have locked it to these versions (although older versions still work).
+This was necessary because this major release of OpenFL includes breaking changes that make it non-trivial for Flixel to support:
+drawTiles
API has been removed - this is the API used by Flixel for rendering on native targets. There is a replacement API called Tilemap
, but it doesn't support all features Flixel needs / drawTiles
did.Flixel will definitely be compatible with OpenFL 4 in the future, but this will take some time.
+ + ++ The Indiegogo campaign of 2016 was a huge + success! +
+ +HaxeFlixel is developed by and is now furtherly funded by, a community of +people who have a geniune passion for the project.
+We thank everyone for their contributions over the past 5 years, +whether it has been spreading the word, fundraising, admin, testing, +reviewing & submitting code or teaching and supporting others. +All this keeps inspiring the community to continue HaxeFlixel to help +make cross platform games easier and free!
+Also, none of this fundraiser would have even started without the dedicated effort from +Lars Doucet of Level Up Labs +leading this entire campaign, so a big thanks to Lars!
+With the larger donations made in the campaign, it has also made it possible for HaxeFlixel to have +official sponsors! After we gather all the logos for the page, we will add a dedicated sponsors page.
+Read more about the campaign on +our Indiegogo page.
+ + +October 11, 2016 + +
We've just released HaxeFlixel 4.2.0! There are a lot of small additions and improvements, like exposing the length
of FlxSound
objects or allowing you to configure their endTime
.
Apart from that, we also fixed a lot of bugs and took care of some old annoyances, like not being able to control which tweens or timers are paused when entering a substate (see #1934 for a detailed explanation + usage example). This is also one of the improvements that caused some minor breaking changes.
+The debugger overlay became even more powerful (and can now be opened with F2
):
You can find the full changelog here.
+Thanks to everybody who contributed to this release in any way!
+By the way, in case you hadn't noticed: we recently released new versions of flixel-tools and flixel-templates as well, adding support for Visual Studio Code!
+Don't worry, it's still coming! There are two reasons why it's not a part of 4.2.0:
+We're aiming to have support for OpenFL 4 starting with the next minor release (4.3.0).
+ + +March 2, 2017 + +
Hey everybody, Tim here to share something with you: Does this sound familiar?
+++"I wish I could remember how to do this one, small thing in HaxeFlixel, and that I didn't have to dig through a massive tutorial just to jog my memory."
+
Or, how about:
+++"What's the 'right' way to handle this, again?"
+
Yeah, we've all been there! Wouldn't it be nice if there was some kind of interactive repository of just about every conceivable useful snippet of code that you could use to answer these, and other questions?
+Well, what if I told you that it already exists!?
+ +A couple of years back now (wow, time flies!), I had the idea of putting together a website that would showcase a ton of little samples of HaxeFlixel code, along with a demonstration of that code in action. It took some time, but I was able to get a pretty good start on this thing, and I called it HaxeFlixel Mechanics.
+ +The plan was to get a bunch of samples in the project, and then invite collaborators to add more to help grow and flesh it out... and then Real Life™ got in the way and the project got pushed aside...
+This year, let's work together to get HFMechanics back up and running again. Let's add more demos and snippets, and try to fill it with lots more good stuff.
+If you'd like to lend a hand, join us over in the repository. Add an issue for suggestions or requests, or submit pull requests for snippets you've created so we can add them to the site! You can find more detailed information about contributing here!
+If you want to check out the site in action, you can visit http://hfmechanics.nfshost.com/ for a live version!
+Thanks! ...and be sure to tell your friends!
+ +++ + +Tim is a versatile developer from St Louis, Missouri, who has been using and contributing to HaxeFlixel since at least 2013.
+
May 13, 2017 + +
Hey, Beeblerox here!
+As you may know, HaxeFlixel is currently stuck with outdated versions of OpenFL and Lime due to some rendering incompatibilities, and updating it has turned out to be more work than expected. I want to give you a quick status update regarding my work towards making Flixel compatible with the latest versions of OpenFL again.
+Note: I've switched to working on a new branch and closed the previous pull request. You can follow my progress in the new pull request here.
+I've added a FlxMaterial
class (see here). Currently, it's just a single render-pass material with multiple textures support. This enables more complex effects like 2D-lighting (by using normal maps generated with tools like SpriteIlluminator). I've updated the flixelighting lib by Pixelbear to make it work with the new material system on my fork here.
Here is an example of how it can be used right now:
+// create regular sprite
+shadedWall = new FlxSprite(100, 200, "assets/rock.png");
+add(shadedWall);
+// create custom lighting material and apply to our sprite
+lightMaterial = new FlxLightingMaterial();
+teapot.material = lightMaterial;
+// tune light material properties
+lightMaterial.setAmbient(FlxColor.YELLOW, 0.2);
+// add light source
+light = new FlxLight(0, 0, 0.3);
+light.lightColor = FlxColor.WHITE;
+add(light);
+lightMaterial.addLight(light);
+// and create normal map and apply it to our material
+var normalMap:FlxNormalMap = new FlxNormalMap(0, 0, "assets/rock_n.png");
+lightMaterial.addNormalMap(normalMap);
+
+And here is the result:
+ +Now a few more details about sprite materials.
+Each sprite, tilemap, etc. now has a material:FlxMaterial
property. A material stores information about an object's blend mode, smoothing, shader to use, data for the shader (values of shader uniforms) and an array of textures to use. So when you do sprite.blend = BlendMode.ADD;
, in reality you change the blend mode of its material.
By default, objects have materials without shaders (shader is null
), which means that the renderer will use default shader and batch this object. If you do set a material's shader property, then the material will create a data object (openfl.display.ShaderData
) to store info about shader uniforms which you could set later. Say your shader has a uFill
uniform after setting material.shader = myCustomShader
, you can set the value of this uniform like this:
material.data.uFill.value = [0.5];
+
+Setting a material's shader you will break the batch, but if several sprites share the same material, then they will be batched together again.
+The material class also has a batchable:Bool
property which is true
by default. This means that the renderer will try to batch objects with this material. But in case you have many sprites with different materials and you want them not to be batched (to minimize the amount of data which will be reuploaded to the GPU), you could set it to false
to force this behavior (sprite.material.batchable = false;
).
For more complex effects which require multi-pass rendering, I've added FlxRenderTarget
which extends the FlxSprite
class. Basically it's the same as a FlxSprite
, but you can render other sprites to its texture through underlying OpenGL calls (not with BitmapData's draw()
method). I took the idea from Phaser's RenderPass object, which has a very simple API. Here is a usage example for it:
// create render target with the size of 256 by 512 pixels
+var renderTexture:FlxRenderTarget = new FlxRenderTarget(256, 512);
+// specify camera which will be used for calculation of drawable sprites positions on this render texture.
+renderTexture.renderCamera = FlxG.camera;
+add(renderTexture);
+// set object's renderTarget, so it will be rendered only on its texture and won't appear on any camera.
+teapot.renderTarget = renderTexture;
+// set render pass shader.
+renderTexture.shader = myCustomShader;
+
+Since the first iteration of the new renderer I've changed a lot of things. The biggest of them is the way objects are rendered to the camera and to the screen. Now, each camera has its own render texture to which all object are rendered, and then this texture is rendered to the screen.
+ +This way of rendering helps minimize the number of array iterations, and also made it much easier for other features to be implemented (such as the FlxRenderTarget
class).
I was disappointed by the performance of drawDebug
rendering on native targets, so I've redone it and now it uses OpenGL instead of OpenFL's Graphics
API.
Last weekend I was busy with rewriting the FlxStrip
and FlxTrianglesData
classes. They are responsible for rendering complex meshes having hundreds of vertices.
+As you may know, FlxStrip
is a subclass of FlxSprite
. It's only purpose was to call the drawTriangles()
method with vertices
, uvs
, indices
and colors
arguments specified by the user. Now, it's become much more flexible and easier to use for prototyping.
+Each FlxStrip
object has a data:FlxTrianglesData
property which stores information about added vertices. In addition to getters and setters for vertices
, indices
, etc., it now has utility methods for adding a single vertex and a single triangle:
var data:FlxTrianglesData = mySprite.data;
+// set vertices info in old way
+data.vertices = Vector.ofArray([0.0, 0.0, 100.0, 0.0, 0.0, 100.0]);
+data.colors = Vector.ofArray([FlxColor.RED, FlxColor.GREEN, FlxColor.BLUE]);
+data.indices = Vector.ofArray([0, 1, 2]);
+// and you can add it new way
+data.start();
+data.addVertex(200, 300, 0, 0, FlxColor.RED);
+data.addVertex(300, 300, 0, 0, FlxColor.GREEN);
+data.addVertex(200, 400, 0, 0, FlxColor.BLUE);
+data.addTriangle(0, 1, 2);
+
+// plus you can change data of individual vertex
+data.setVertex(0, newX, newY, 0.0, 0.0, newColor);
+
+This new API allows to minimize the iterations through inner data arrays which will be uploaded to GPU, which should result in a noticeable performance improvement.
+I also borrowed HaxePunk's code for GPU-accelerated rendering of graphics primitives (Draw
class) and adapted it to Flixel, so there is new FlxDraw
class for rendering lines, rectangle, circles, polygons and curves.
Next, I'll start updating the flixel-addons classes and demos. Meanwhile, I'd be happy about any feedback you have for me - please post it in the OpenFL 5 compatibility pull request!
+ + +August 26, 2017 + +
Hi everyone!
+I'm Nick (@MSGhero16), and I'll be working for the HaxeFlixel community now. My goal is to drum up engagement with our favorite library by writing regular blog posts and creating new demos.
+If you're new around these parts, here's a brief intro to what this is all about.
+HaxeFlixel is a free cross-platform, open-source library that makes your game-making easier. It's a modernized version of the original Flixel library for Flash, which was hugely popular back in the day for how easy it was to get started with.
+(I'll be referring to HaxeFlixel as Flixel or HF)
+To get started making your first game or your 30th prototype, check out the Getting Started page.
+I started coding with Flash about seven years ago, inspired by Newgrounds games like Super Chibi Knight and Castle Crashing the Beard. I made a few games that did pretty well overall, keeping the coding as a hobby for my spare time.
+I moved on to Haxe when I learned I could get HTML5 and CPP builds for (almost) free. The transition from AS3 to OpenFL was as smooth as silk, and I quickly found and exclusively started using Flixel. I am currently working on a new game, Enki Adventures, in addition to numerous little demos and projects that may one day see the light of day.
+Flixel is great for me because I no longer have to even think about updating and rendering. Input is taken care of; spritesheets are handled. Life is good.
+I'm also helping with a UI library called HaxeUI that I'll be talking about in the future, specifically the Flixel backend (haxeui-flixel) that ties into the core library. Stay tuned for that one.
+ +People love this library; it's tied with its dependency as the most popular haxelib on GitHub. It's used in many Ludum Dare games, and lots of devs swear by it. It's rather easy to pick up, with all the demos highlighting each feature.
+So as my first demo for the community, I'm thinking about making a game. Something simple, open-source (of course), fun, and kinda cool. I plan on publishing it as an HTML5 game on Newgrounds and on other portals. That way, the demo will get plenty of views outside of its typical audience. Even more if it's actually good and gets front-paged, but y'know, one step at a time.
+At least on NG, I am seeing a lot of Flash anxiety from devs, especially from people who are just getting started with making games. Spreading the gospel of Flixel is something I am increasingly doing there, and the demo seems like a great next step. I believe that current and future devs can only benefit from giving Flixel a try.
+From the successful IndieGoGo campaign, it's clear that there is interest in seeing Flixel improve. Aside from that, however, contributions to the library as well as community management have been performed out of the infinite goodness of people's hearts.
+ +Ultimately, we'd like to drum up support for our * Patreon *, to give more people more reason to contribute. At the moment, the plan is to allocate funds to people who put in the hours on GitHub or in the general community, as well as positions to increase community size and support, like mine.
+More people making more games with better versions of Flixel. That's what it's all about.
+ + +September 17, 2017 + +
So you've downloaded Flixel, skipped around in the tutorial, and are wondering what's next. You could start making that game you've been thinking about. Or, better yet, make that game while also talking to other people who are using Flixel! They're not scary, I promise.
+There are a number of places where you'll find fellow Flixel-ers. It's really up to you where you want to hang. Here are some of the popular destinations:
+If you're new and having a bit of trouble getting into things, the forums are a great place to start. Not only will any of your questions be answered, but you also have a nice place to start posting about what you're working on. Get some eyeballs on your cool new content and see what others are up to.
+ +The forum also just looks really nice. Like, come on. That looks so good.
+Slack and Discord are both chat apps. Slack is often used in companies as a team messenger. Discord is built for gamers, with voice chat capabilities. As far as Flixel goes, the Discord channel has more activity, but "activity" can sometimes include 200 off-topic posts about philosophy. The Slack channel is more focused but quieter. Check 'em both out and see for yourself! It's 100 percent possible to be a part of both, even if Kantian philosophy isn't your thing.
+Click here to join the Discord. Slack requires an invitation, so feel free to message me on Twitter (@MSGhero16) for one.
+Both Slack and Discord have desktop apps, web interfaces, and mobile apps. Flixel community on any device.
+#HaxeFlixel. That's all you need to be a part of this one. Add it to your tweets to let everyone know that you're using an awesome library. The main Flixel account will see this and might retweet your posts, extending your reach. Search that hashtag to find what everyone else is up to, maybe find some inspiration or new devs to follow. Tweets even show up in the Slack and Discord channels!
+Twitter has the most open audience of the community hangouts: not everyone on Twitter has heard of Flixel. So help spread the word!
+GitHub is less of a hangout than the others. It's where the code of Flixel is. The purpose of GitHub is for the community to find issues in the code and help fix them. Now, "issues in the code" and "issues in your code" are two different things. If you're struggling or having problems, the forums, Slack, or Discord are definitely better options if you're new to this kind of thing.
+ +An example of GitHub being used properly is this code fix. The code of Flixel was actually incomplete, not being able to handle a specific use-case. So a member of the community brought it up and then fixed it. If you're interested in contributing in this way, there are numerous resources you can look up for how to use GitHub and version-control software. Or ask someone in the Slack/Discord!
+For an itemized version of this post, check out our community docs page. It also mentions a few more, less popular interaction channels such as our IRC and our Subreddit.
+So there you have it. A good number of ways to talk to everyone else using Flixel. Get to chatting!
+ + +March 28, 2018 + +
We have a new demo made by yours truly Nick: a puzzle game of reflecting light and combining colors. Featuring...
+In addition to being a fully playable game, the FlxLightPuzzle Demo heavily features the FlxVector class. Vector math is a nightmare, and I was actually surprised to see how much of it FlxVector handles for you.
+The source code is here, which you're free to look at and edit to make your own version of the game. Maybe you add more levels, real graphics, or more obstacles that the player has to solve around. I'll leave that to you.
+I think color-based mechanics and reflection-based mechanics are super cool, and that's about all the backstory there is. The graphics are simple shapes (FlxSpriteUtil) to avoid any fuss about art, and the music is a random find that I happened to enjoy (used with permission, of course).
+ +You'll quickly notice that you can play the game in RBY, RGB, or CMY color spaces. I couldn't really decide whether RBY+orange/green/purple or RGB+yellow/cyan/magenta would be better to play in. On one hand, you have the "normal" primary colors that everyone is used to; on the other, there are the "correct" primary colors for light. So I added both, and CMY for anyone in the printing business playing the game.
+ +On the third level, you learn that you can mix primary colors to hit secondary targets. On the fourth (my favorite), you learn that secondary colors can't hit primary targets. It wasn't always like that! I asked a number of people who said that it lessened the challenge if, say, green light could hit yellow targets. When that mechanic was changed, I thought that made level four impossible to solve. It was pure coincidence that it resulted in clever level design.
+Iterating to improve is a big part of level design. I've played through each level dozens of times now, fiddling with numbers and colors until I got to a pretty good result. The first and last levels haven't changed since day one — and you'll see why when you play through — but the middle eight have been through a lot. I still have fun every time I play, even if I can (almost) play through the whole thing without any do-overs. I call that a success.
+You are free to do whatever you want to the code under the MIT license. There are a number of things to learn from, such as vector math, object pooling, subtle tweening effects, physics and optics, and level data files. I added way more comments than I normally do to describe some of that. If you want to keep it simple, though, editing the levels or adding new ones is your best bet. There are a lot of places the code can go, and I limited the scope of the demo so that you could be creative with a solid foundation.
+So, go be creative! Don't forget to check out the other demos as well. There is plenty to learn from. I'll be back soon with a look at a UI library that recently got Flixel support.
+Stay tuned.
+ + +May 4, 2018 + +
HaxeFlixel 4.4.0 is now available on Haxelib, adding support for OpenFL 8 and Lime 6.3.0! Breaking changes are mostly limited to blend modes and a slightly different shader syntax. Additionally, 4.4.0 is still fully-backwards-compatible with OpenFL 3.6.1 legacy or next, so even if you don't plan on upgrading to OpenFL 8 just yet, don't let that stop you from taking advantage of the other fixes and improvements in the 4.4.0 release.
+OpenFL 5 6 7 8 support has been long-awaited. OpenFL 3.6.1 is still working fine for many people, so why be excited about this? Well, there's many reasons:
HTML5 support in OpenFL 8 is vastly improved. WebGL is now the default renderer, which also means - GLSL shaders by default:
+Check out the demos page, we've updated all of them with OpenFL 8 builds so you can see the improvements in action.
+We can now take advantage of all the fixes and improvements that happened between OpenFL 3.6.1 and OpenFL 8 - this has already meant that tens of old issues could be closed across the issue trackers of Flixel repositories.
+OpenFL 3.6.1 will eventually stop working with latest Haxe - there's already some minor compiler errors with the latest Haxe development branch. A similar issue exists in the Android world, with Ant being switched out for Gradle as the build tool. Latest HXCPP was also causing some trouble.
+Latest OpenFL enables HaxeFlixel to use the HashLink target in the future, once support for it is implemented in Lime. If you haven't heard about HashLink, it's a new Haxe target and VM that's Neko's spiritual successor, but much faster. It has amazing features such as source-level debugging and native C compilation.
+Since these reasons have surely managed to excite you as much as us, what else do you need to know about OpenFL 8? Here's a few things:
+haxelib upgrade
. Alternatively, you can run haxelib update
manually on the different libraries.Finally, a huge thanks goes to Joshua Granick, the maintainer of OpenFL. He invested a lot of time to make sure that this transition can happen smoothly. The fairly short diff of the pull request on the Flixel end of things doesn't begin to do all the behind-the-scenes improvements and bugfixes that happened in OpenFL and Lime justice!
+That's all for now. If you have any more questions, get in touch with the community. Keep on making awesome games!
+- The HaxeFlixel team
+P.S. Don't forget to check out the livestream of the Haxe US summit that is going on right now!
+ + +September 1, 2018 + +
Hi there, I’m Troy (@RouStudios), creator and maintainer of the HaxeFlixel backend for DragonBones. I’m here to write a post about DragonBones support for HaxeFlixel and how to use it.
+First of all, what is DragonBones? DragonBones is a free *open source alternative to the popular 2D bone animation tool Spine (which Flixel already has support for). Its a tool which allows you to animate static 2D images programmatically (similar to Flash tweening) without having to painstakingly animate each individual frame in a spritesheet. Why would one use it over Spine? Well Spine can potentially be quite expensive for new indie devs, and DragonBones is free. Not only that, DragonBones is a fully featured, intuitive bone animation editor and has Mesh Deformation support just like Spine. Quite a deal for free!
+ +*One caveat is that DragonBones is not truly open source (if you care about that sort of thing), only the runtime is. If you want a truly open source editor which can export to the DragonBones format while also being fully featured, check out the great alternative COA Tools for Blender. Its DragonBones export should also work with the Flixel backend I’ve created.
+How exactly do you use DragonBones with Flixel? Well, that’s what I’ll be showing you, so let’s get started.
+First install the library from haxelib using:
+haxelib install dragonbones
+
+Although it’s better to install directly from GitHub in order to always keep up with the latest updates:
+haxelib git dragonbones https://github.com/openfl/dragonbones
+
+Then create a new Flixel project template with Flixel Tools, and add DragonBones to your Project.xml
. You can also download the sample project instead to follow along.
<haxelib name="dragonbones" />
+
+Then create a new animation using DragonBones. For the sake of this tutorial, we’re going to use a premade project that comes with DragonBones called DragonBoy that you can select from the starting menu. Once you’re done, go to File > Export
and export the animation using Data Version: 5.0
and Image Type: Texture Atlas
. Then save it to the assets
folder in your Flixel project.
First, import the packages we'll be using.
+import haxe.Json;
+import openfl.Assets;
+import flixel.FlxG;
+import flixel.FlxState;
+import flixel.group.FlxGroup;
+import dragonBones.objects.DragonBonesData;
+import dragonBones.flixel.FlixelTextureAtlasData;
+import dragonBones.flixel.FlixelArmatureDisplay;
+import dragonBones.flixel.FlixelArmatureCollider;
+import dragonBones.flixel.FlixelFactory;
+import dragonBones.flixel.FlixelEvent;
+import dragonBones.animation.WorldClock;
+
+Then, inside the create
function under your FlxState
class you have to create a FlixelFactory
which generates flixel objects for DragonBones like so:
var _factory = new FlixelFactory();
+
+Then you have to use the factory to parse the animation files that you’ve exported in order to read their data.
+var dragonBonesData:DragonBonesData = _factory.parseDragonBonesData(
+ Json.parse(Assets.getText("assets/dragonboy_flixel_ske.json"))
+);
+
+_factory.parseTextureAtlasData(
+ Json.parse(Assets.getText("assets/dragonboy_flixel_tex.json")),
+ Assets.getBitmapData("assets/dragonboy_flixel_tex.png")
+);
+
+Then you have to create a FlxGroup
which will contain all the DragonBones Flixel Sprites. You can do so by declaring a variable and then assigning the new FlxGroup
to it which you will generate using the factory.
var armatureGroup = _factory.buildArmatureDisplay(new FlixelArmatureCollider(250, 250, 27, 25, 13, 8), dragonBonesData.armatureNames[0]);
+
+One thing to note is you also have to pass in a collision box similar to the Flixel Spine plugin. This is because with many different sprites, if you want to check the collisions of the entire “character” then you have to have one large collision box. You also pass in the name of the armature (the animations skeleton). Generally you’ll only have one armature, so just pass in the first index of the armatureNames
array from the data you got earlier. Otherwise, check your animation in DragonBones to find the name and pass it in as a string.
Then you iterate through all of the sprites in the FlxGroup
to set their initial properties (such as scale, placement in the world, ect.) as you please.
armatureGroup.forEach(function(display:FlixelArmatureDisplay) {
+ display.antialiasing = true;
+ display.x = 100;
+ display.y = 100;
+ display.scaleX = 0.50;
+ display.scaleY = 0.50;
+});
+
+Now you do the same thing to start the initial animation. Again we grab the first index from the list of animation names to keep it simple.
+armatureGroup.forEach(function(display:FlixelArmatureDisplay) {
+ display.animation.play(display.animation.animationNames[0]);
+});
+
+Then, inside the update loop of the FlxState
class, add FlixelFactory._clock.advanceTime(-1);
to update the animation clock so that the factory knows what point it's at on the timeline.
override public function update(elapsed:Float):Void
+{
+ FlixelFactory._clock.advanceTime(-1);
+ super.update(elapsed);
+}
+
+Finally, we add the FlxGroup
to the FlxState
inside the create
function so it can be rendered!
add(armatureGroup);
+
+That’s it! Once you compile with lime test html5
, it should look like this:
March 25, 2020 + +
Newgrounds is hosting a HaxeFlixel game jam with a $1,300 prize pool! The theme is "Alone, Together" and submissions have to be uploaded to Newgrounds by Monday, April 27th with the tag "HaxeFlixelJam" included. For more information, check out their official announcement.
+ +We also modernized our Tutorial to use Ogmo 3, Visual Studio Code and HTML5, so it's a great time to learn HaxeFlixel if you're new to it! If you get stuck, just hop onto our Discord channel.
+Let's make some amazing games!
+ + +June 7, 2023 + +
Hello everyone! George, here, gladly announceing the return of the HaxeFlixel blog after a long hiatus. While there have been many ongoing changes to the HaxeFlixel framework, we've sadly neglected to broadcast those changes on the website. Let's take this time to get everybody up to speed with all we've done these last 3 years.
+Since the last release blog post, we have released 24 new versions of HaxeFlixel! There's too many changes to go over in just one blog post, so if you're far behind, be sure to look at the changelog for details on every release. I do want to go over some big milestones from these past few years.
+HaxeFlixel's first major release in over 6 years! Be sure to check out the Migration Guide for more info on the bigger changes as well as info on how to upgrade your old projects from version 4. Here's a brief list of the changes (the changelog has them all, of course)
+Most people never knew we had a FlxVector class with a bunch of incredibly handy math operations, so we moved them all into FlxPoint.
+One small tweaks to preserve momentum and a new feature that let's objects ride other non-immovable objects. The dirty minds following us on Twitter got really excited about this. +
+October 2, 2023 + +
We've finally released Haxeflixel 5.4.0, our largest release in quite some time, arguably larger than the 5.0.0 release! This release focuses heavily on assets and animations, like the new FlxAsepriteUtil and the new Multi-Atlas system.
+We try to release new versions of HaxeFlixel around once a month, and have been, for the most part. This one took much longer, the previous version, 5.3.1, was released in early May, that's almost 5 months! The main reason is because we really wanted to get things right, The Aseprite utils, alone, add around 20+ new types and it's not good to release them prematurely, only to rename, reorganize or retool them in a later patch. This ended being a good call, since we did end up iterating on and organizing these utils, later.
+Another reason the release was delayed was that George, our lead maintainer finally went on his honeymoon. He didn't feel like touching a computer while visiting his motherland for most of September, with views like this, I'm sure you wouldn't want to either!
+ +By far the biggest development of HaxeFlixel 5.4.0 is the various tools that utilize Aseprite's atlas exporting tools. For those unaware, Aseprite is a very popular animated sprite editor and pixel art tool, we can't recommend it enough for anyone using HaxeFlixel, especially for pixel-art games. The main goal of FlxAsepriteUtil is to allow devs to define animation data using Aseprite's tags rather than in code.
+ +(Made using the Animated Pixel Adventurer set by rvros)
+The resulting atlas can easily be applied to a sprite with the following code using the addAseAtlasTagsByPrefix or addAseAtlasTagsByIndex to create an animation for every tag in your .aseprite file.
+hero = new FlxSprite(50, 50);
+hero.loadAseAtlasAndTagsByPrefix("assets/images/adventurer.png", "assets/images/adventurer.json");
+hero.animation.play("idle");
+add(hero);
+
+You can expect more Aseprite focus tools to come now that we've created handy typedefs for Aseprite atlas metadata. One future plan is to use Aseprite's slicing feature to generate 9-slice data or per-frame hitboxes, but it's much easier for you to use this data to roll your own features, for example to see per-frame slice data on an atlas's labelled "attackRect":
+var atlasData:AseAtlas = Assets.getText(myAtlasJsonPath);
+for (slice in atlasData.meta.slices)
+{
+ if (slice.name == "attackRect")
+ {
+ for (key in slice.keys)
+ {
+ // store per-frame slice data
+ trace('frame: ${key.frame} bounds: ${key.bounds}');
+ }
+ }
+}
+
+We have immediate plans to utilize more Aseprite fields, such as the tags' "Repeat" and "Animation Direction" fields. You can expect those in the next release (Update (5.4.1): this change has been added!).
+This feature was specifically made with our Funkin' devs and modders in mind. As the demand for HD atlases with large amounts of animations increase, their atlas image size grows well beyond the limit that flixel can currently handle. By combining multiple atlases at runtime devs can better compartmentalize their animations, ultimately allowing FlxSprites to have more animations without having to switch graphics every time you change a sprite's animation. Splitting up animations also allows you to load them separately, for instance, if you only need certain animations on certain levels.
+This project's art pipeline could really benefit from multi-atlas since each character is made from about a dozen different aseprite files, each with potentially dozens of frames.
+ +(Art by Adam V., you should [hire him](https://twitter.com/Koolboyman/status/1672366422767591424)!) +Here's an example of how to add atlases to another
+// create an atlas for each file
+var idleAtlas = FlxAtlasFrames.fromAseprite('assets/images/snowman-idle.png', 'assets/images/snowman-idle.json');
+var jumpAtlas = FlxAtlasFrames.fromAseprite('assets/images/snowman-jump.png', 'assets/images/snowman-jump.json');
+var walkAtlas = FlxAtlasFrames.fromAseprite('assets/images/snowman-walk.png', 'assets/images/snowman-walk.json');
+var blockAtlas = FlxAtlasFrames.fromAseprite('assets/images/snowman-block.png', 'assets/images/snowman-block.json');
+
+// combine all the atlases by adding to the idle atlas
+idleAtlas.addAtlas(jumpAtlas);
+idleAtlas.addAtlas(walkAtlas);
+idleAtlas.addAtlas(blockAtlas);
+
+// create the FlxSprite
+var snowman = new FlxSprite();
+snowman.frames = idleAtlas;
+
+// add all the anims
+snowman.animation.addByPrefix("idle", "snowman-idle_anim");
+snowman.animation.addByPrefix("jump", "snowman-jump_anim");
+snowman.animation.addByPrefix("walk", "snowman-walk_anim");
+snowman.animation.addByPrefix("block", "snowman-block_anim");
+
+// add it to the state
+add(snowman);
+
+We've added a timeScale field to FlxAnimationController. This can be used to slow down or speed up animations. For instance, you can change a walk cycle's animation speed to match the sprite's changing movement speed, or you can easily implement a power up or skill that increases attack rate. Here's an example of 4 sprites using different timescale properties whenever the attack animation is played:
+ +Update (5.4.1): We've also added the timeScale
field to each individual animation, rather than just the animation controller.
Lastly, we've added the allFiles
field to classes generated via FlxAssets.buildFileReferences, AKA: AssetPaths
. This is just an easy way to iterate or search a list of every asset included in this build. if you don't like the name allFiles
you can specify a custom name in the listField
arg of buildFileReferences
.
FlxAseAtlasUtils
and FlxAnimation
.+ We've finally released Haxeflixel 5.4.0, our largest release in quite some time, arguably larger than the 5.0.0 release! This release focuses heavi... + read more +
+ + ++ Hello everyone! George, here, gladly announceing the return of the HaxeFlixel blog after a long hiatus. While there have been many ongoing changes ... + read more +
+ + ++ Newgrounds is hosting a HaxeFlixel game jam with a $1,300 prize pool! The theme is "Alone, Together" and submissions have to be uploaded ... + read more +
+ + ++ Hi there, I’m Troy (@RouStudios), creator and maintainer of the HaxeFlixel backend for DragonBones. I’m here to write a post about DragonBones supp... + read more +
+ + ++ HaxeFlixel 4.4.0 is now available on Haxelib, adding support for OpenFL 8 and Lime 6.3.0! Breaking changes are mostly limited to blend modes and a ... + read more +
+ + ++ We have a new demo made by yours truly Nick: a puzzle game of reflecting light and combining colors. Featuring... +FlxLightPuzzle Demo + +In addition ... + read more +
+ + ++ So you've downloaded Flixel, skipped around in the tutorial, and are wondering what's next. You could start making that game you've been thinking a... + read more +
+ + ++ Hi everyone! +I'm Nick (@MSGhero16), and I'll be working for the HaxeFlixel community now. My goal is to drum up engagement with our favorite librar... + read more +
+ + ++ Hey, Beeblerox here! +As you may know, HaxeFlixel is currently stuck with outdated versions of OpenFL and Lime due to some rendering incompatibiliti... + read more +
+ + ++ Hey everybody, Tim here to share something with you: Does this sound familiar? + +"I wish I could remember how to do this one, small thing in Ha... + read more +
+ + ++ We've just released HaxeFlixel 4.2.0! There are a lot of small additions and improvements, like exposing the length of FlxSound objects or allowing... + read more +
+ + ++ HaxeFlixel is developed by and is now furtherly funded by, a community of +people who have a geniune passion for the project. +We thank everyone for ... + read more +
+ + ++ HaxeFlixel 4.1.0 has just been released to Haxelib (as well as new versions of flixel-addons, ui and demos)! The full changelog contains a long lis... + read more +
+ + ++ We are proud to announce the release of HaxeFlixel 4.0.0! This is without a doubt the biggest release yet, with nearly 2000 new commits on the core... + read more +
+ + ++ Hello HaxeFlixel enthusiast, gamedevsam here! +I have some big news to share with the HaxeFlixel community: HaxeFlixel is now on Patreon. +Patreon is... + read more +
+ + ++ Hi, gamedevsam here, I'm a developer and evangelist of HaxeFlixel. +The following blog post was written by Beeblerox (the original creator of HaxeFl... + read more +
+ + ++ The HaxeFlixel project and it's active community of game dev enthusiasts, have opened this +blog up to post about the latest updates, news and anyth... + read more +
+ + +HaxeFlixel port of Timothy Hely's "Using-BSP-Trees-to-Generate-Game-Maps".
+ + +Example of how some blend modes can be replicated using GLSL shaders.
+ + +A very simple, but solid Breakout clone originally created by Photonstorm (Richard Davey) in 20 minutes.
+ + + +An interactive calculator implemented using hscript.
+ + +A port of the Collision and Grouping demo by Zachary Tarvit featured on flixel.org/features.
+Game objects in Flixel are can be stored in FlxGroups. Groups can be added to the game state to help automate and organize updating, drawing, collisions, camera control, scroll amounts, and more. When you want to avoid calling resource-intensive functions like FlxG.collide() more than a few times in each game loop, you can use nested groups as a way to simplify those calls. For example, let's say your game objects are divided into three different groups: Apples, Pears, and Bananas. And you want to see if the fruit have landed on the ground yet. You might be tempted to call FlxG.collide() three times: FlxG.collide(Apples, ground); FlxG.collide(Pears,ground); and so on. However, you can create a fourth group, called simply Fruit, and add each of the other groups to it. Then you can just call FlxG.collide(Fruit, ground); and you should see your performance improve notably.
+ + +A demo showcasing keyboard navigation with a flixel-ui instance. You can navigate +between buttons and click on them, as well as change the allowed input methods.
+Controls:
+Mouse: Click on stuff +Keyboard:
+Check "Wrap" to turn wrapping on and off.
+ + +EZPlatformer is a really simple platformer which was originally created by Adam "Atomic" Saltsman as a tutorial for creating platformers with flixel.
+ +Use WASD / the arrow Keys to move the red square around. Your objective is to collect all coins, at which point an exit appears.
+ + +Demonstrates how to use file browsing on both Flash and Native (CPP/Neko) targets. The Flash target uses FileReference.browse(), while the Native target uses the systools.Dialogs.openFile(). The Native targets depend on the systools library, available on haxelibs.
+Use the mouse wheel to zoom in and out, or the number keys to use a specific zoom level (1-5).
+ + +Controls: Spacebar
+A cross-platform port of AdamAtomic's Flappybalt, converted to Haxe and HaxeFlixel. You can play this on your Android by downloading it from the Google Play Store if you'd like.
+ + +This demo showcases how to use FlxTilemapExt and FlxTileSpecial to flip, rotate and animate tiles.
+ + +A side-scrolling shooter. Made by @SeiferTim during a 30-min presentation on how easy it is to make prototypes in HaxeFlixel.
+ + +Showcases the flixel.input.actions
API added in Flixel 4.6.0.
Showcases loading of an Aseprite [Sprite Sheet](https://aseprite.com/docs/sprite-sheet/#texture-atlases tools) using flixel.graphics.FlxAsepriteUtil API added in Flixel 5.4.0. This demo uses the loadAseAtlasAndTagsByIndex method to create animations.
+ + +Demonstration for the FlxAsyncLoop
class, created by SeiferTim (Tim I Hely).
+This utility allows you to setup a loop in a way that still allows update()
and draw()
to be called so you can show progress bars or whatever, instead of the game freezing and locking up until the loop has completed.
+This demo will generate 5000 random little squares, showing a progress bar as it does so.
An example of flixel.graphics.atlas.FlxAtlas
.
This is a HaxeFlixel port of the BunnyMark Benchmark.
+The initial BunnyMark was created by Iain Lobb (code) and Amanda Lobb (art), then ported to haxe-NME by Joshua Granick, then enhanced by Philippe Elsass, now ported to HaxeFlixel by BeebleRox, and improved by impaler and Gama11.
+Flash is limited to software rendering so there is a significant performance improvement when you use cpp targets. Cpp targets make use of drawTiles() GPU Acceleration and on a desktop it can display 10 000's of bunnies with additional variations of alpha and scaling. On the flash target however, rendering with alpha and scaling means a severe performance decrease.
+ + +This demo showcases different flixel camera features like zoom, lerp, lead and follow styles.
+Demo created by ProG4mr after adding camera features : lerp and lead.
+For full-window zoom out, look at the commented source code, note that fps drop significantly.
+ + +A demo showing off the capabilites of FlxCaveGenerator.
+Generating a cave for a FlxTilemap is really simple:
+var caveData:String = FlxCaveGenerator.generateCaveString(Width, Height, SmoothingIterations, WallRatio);
+
+The String generated can be used directly in FlxTilemap's loadMap()
method.
You can use WASD or the arrow keys to move the little character around. :)
+ + +An example of flixel.addons.effects.FlxClothSprite
.
A port of FlxCollisions created by Adam Atomic, showing off flixel's 2D physics capabilities.
+ + +An example of the flixel.addons.effects.chainable
package.
An example game using the finite state machine implementation in flixel.addons.util.FlxFSM
.
A simple invaders game in flixel originally created by Adam "Atomic" Saltsman. Link to the original GitHub repo. The source code is extremely well documented and thus very hepful for beginners.
+Controls:
+Left / right to move and spacebar to shoot.
+ + +FlxMouseEvent allows FlxSprite
s to listen to mouse events like MouseDown
, MouseUp
, MouseOver
and MouseOut
.
To activate it, simply add sprites and implement the event callbacks:
+import flixel.input.mouse.FlxMouseEvent;
+
+var sprite = new FlxSprite();
+FlxMouseEvent.add(sprite, onMouseDown, onMouseUp, onMouseOver, onMouseOut);
+
+function onMouseDown(sprite:FlxSprite) {}
+function onMouseUp(sprite:FlxSprite) {}
+function onMouseOver(sprite:FlxSprite) {}
+function onMouseOut(sprite:FlxSprite) {}
+
+
+
+ This demo showcases the integration of the Nape Physics Engine with HaxeFlixel.
+Thanks to ProG4mr for his work with these Demos and his work providing FlxNapeSprite and FlxNapeState. See the flixel-addons repository.
+Thanks to Henry-T for his amazing CutUp demo!
+ + +A port of Nape's DestructibleTerrain sample to HaxeFlixel.
+ + +An example of flixel.addons.nape.FlxNapeTilemap
.
An example of flixel.addons.editors.pex.FlxPexParser
. It creates a FlxEmitter
from a .pex
file created with the Pex Particle Editor.
You can drag your mouse to move the fireball.
+ + +An example of flixel.addons.display.FlxPieDial
.
FlxPongApi demonstrates usage of the FlxGameJolt class in the flixel-addons package, which allows for simple communication with the GameJolt API. The gameplay itself is a sort of roguelike Pong game with various obstacles and enemy types that are encountered as the player progresses. The game is very simple visually, with no art assets and only four colors, with those colors selected programmatically and change very time you play.
+You can play FlxPongApi on GameJolt here to see all the functions in action. You can view the source code for FlxGameJolt here.
+ + +This is simply a collection of functions to test the updated FlxRandom class in HaxeFlixel.
+The buttons on the right side of the display can be used to run a series of tests relating to each function of FlxRandom. Most of the tests will run the same function many times for benchmarking purposes. Others will compare the new class to the previous FlxRandom (which is stored in the source folder as OldFlxRandom), the new FlxRandom without inline, or pure Math.random().
+Some tests may take some time to run, please be patient. Note that the 15-second script timeout has been overridden and set to 60 seconds.
+ + +An example of the flixel.addons.display.shape
package.
Showcases flixel.addons.util.FlxSimplex
(added in flixel-addons 2.7.0).
This is a demo of the FlxSkewedSprite class by Beeblerox. As you probably guessed, it allows you to skew sprites.
+ + +A basic snake game in flixel. Originally created by photonstorm, later ported to HaxeFlixel. Has been largely rewritten by Gama11.
+Controls:
+Arrow keys or WASD to control the snake's movement.
+ + +This is a demo showing off the use of the FlxSpine class to integrate the Spine bone based animation system into HaxeFlixel.
+ + +This demo shows bitmap filters being applied and updated on FlxSprites using the FlxSpriteFilter class.
+ + +FlxTeroids is a simple Asteroids clone. The original GitHub repo by Adam "Atomic" Saltsman can be found here.
+Controls:
+Move - Arrow keys
+Shoot -Space
+ + +FlxTextFormats allow the use of different colors, border colors and font weights on the same FlxText.
+ + +A demo of the sloped tiles possible by using the FlxTilemapExt class created by Peter Christiansen. The original demo can be found here, the original forum thread is here.
+Controls:
+Move - WASD / Arrow keys
+ + +This demo shows the effects that can be accomplished with FlxTrailArea
.
+For every FlxSprite
that is added to the area, a trail effect will be rendered on the area. It's more efficient than a regular FlxTrail
due to the fact that it only uses a single bitmap.
HaxeFlixel has advanced tweening built in for interpolation over time.
+Tweens are a useful part of any framework and its applications are many. It can be game object movements, color changes and even tweening variables over time. The current tween types include:
+There is also an impressive choice of Ease types for every Tween type to choose from:
+This demo showcases the effects that can be accomplished with the use of the FlxTypeText class.
+ + +An example of the flixel.graphics.frames
package.
A demo showcasing HaxeFlixel's gamepad support.
+ + +This showcases a classic retro grid movement.
+Thanks to BuzzJeux.
+ + +A demo showcasing an efficient method to have a lot of objects path to one target, using a heatmap. Inspired by this Gamasutra article.
+Controls:
+As the name would imply, this is a minimalist tower defense game. This was originally created by Gama11 for Ludum Dare 26. You can find the original source code for that here.
+This was ported to the latest version of HaxeFlixel and OpenFL by Steve Richey.
+ + +"A small (but sadly not that simple) demo game built on the flixel framework" by Adam "Atomic" Saltsman, which has later been ported to HaxeFlixel. You can find the GitHub repo of the original AS3 version here.
+Controls:
+Example of applying a GLSL shader to a FlxSprite
to achieve a mosaic / pixelation effect.
This demo builds several layers of randomized objects (FlxTileblock
and FlxSprite
) with varying scrollFactor
values to achieve a parallax effect when scrolling.
A port of the Particles Demo by Zachary Tarvit featured on flixel.org/features.
+In games, "particles" and "particle emitters" refer to a whole class of behaviors that are usually used for special effects and flavor. The "emitter" is the source and manager of the actual "particle" objects that are spewed out and/or floating around. FlxParticle is just an extension of FlxSprite, and FlxEmitter is just an extension of FlxGroup, so a particle system in Flixel isn't really that different from any normal group of sprites. It just adds some special behavior for creating and launching particles, and the particles themselves have some optional, special behavior to bounce more believably in platformer situations. FlxEmitter also has built-in variables that let you specify velocity ranges, rotation speeds, gravity, collision behaviors, and more.
+ + +A port and improvement of the Pathfinding Demo by Bengsiswanto Hendrawan featured on flixel.org/features.
+Pathfinding just means figuring out how to (or if you can) get from A to B. FlxTilemap has a new function FlxTilemap.findPath() which returns a FlxPath object, which is just a collection of "nodes", or FlxPoint objects. Think of it as a list of (X,Y) coordinates in space, going from the starting location to the ending location. Once you have a valid path, you can pass that data to any FlxPath, along with the object it should follow. That function tells the object to start following the path, and you can specify the speed, direction (backward, yoyo, etc), and even tell the object to only follow the path horizontally (handy for objects with gravity applied). These flags mean that you can use paths for more than just character AI - they're also useful for elevators, moving platforms, and looping background animations.
+ + +This demo showcases FlxG.pixelPerfectOverlap()
/ FlxCollision.pixelPerfectCheck()
.
"Project Jumper" has been created by Chipacabra as a tutorial on developing a platformer game with flixel and has later been ported to Haxe(Flixel).
+Controls:
+The original tutorial series by Chipacabra:
+Note that this tutorial is was originally written for AS3-Flixel. The concepts are still valid for HaxeFlixel you can see the entire demo was ported to HaxeFlixel to work the same.
+Project Jumper Part 0: No, seriously, what the heck am I doing?
+[Project Jumper: Interstice](http://Project Jumper: Interstice)
+[Project Jumper Part 3: I love what you've done with the place](http://Project Jumper Part 3: I love what you)
+Project Jumper Part 5: Killing the player, aka Failure Is Always an Option.
+Project Jumper Part 6: Sound; Bleeps Bloops and Frustrated Screams
+Project Jumper Part 8: This isn't stomp at all! Fixing the delay in embedded sounds
+This is a simple demonstration of the flixel-ui engine in a fictional RPG.
+It demonstrates how you can create flixel-ui's from xml layout files, and is also integrated with the firetongue localization library. Not only does this do automatic text replacements, but it also lets you specify UI tweaks for each locale. This example uses both English and Norwegian.
+The Norwegian text on certain buttons is too long and would wrap in an ugly way without adjustment, so there are several tweaks to extend button widths if the current locale is Norwegian.
+flixel-ui has many common UI widgets available, including:
+A port of the Replay demo by Guoboism featured on flixel.org/features..
+Replays are a powerful new feature in Flixel. Replays are essentially a list of what keyboard keys were pressed, and what mouse inputs were given, during a specific time frame. Because Flixel is largely deterministic, we can use that information to recreate a gameplay session that someone else recorded, as long as we have the same SWF. Replays can be used for debugging, arcade-style "attract modes" or in-game demos, and even for cutscenes. Replays can be manipulated using the "VCR" panel on the debugger overlay, or directly through functions like FlxG.vcr.loadReplay(), FlxG.vcr.startRecording(), and FlxG.vcr.reloadReplay(). Adventurous game makers can also check out the more complex Mode source code to see an example of loading a replay from a file to create an "attract mode".
+ + +This is a port of Revenge by Yadu Rajiv. You can find a presentation by him on making games with flixel here.
+Controls:
+Move - WASD or arrow keys
+ + +A port of the Save Demo by Zachary Tarvit featured on flixel.org/features.
+Flash includes a simple way to save data locally, and the FlxSave object allows you to interface with it. This system is not ideal for all things; for example, if you are doing online high scores, this won't really work. If you want players to be able to move and trade save game files, this isn't a good fit either. However, for fast and simple saving of local data, especially things like unlocked progress or user preferences, FlxSave is an easy and built-in option.
+ + +This demo showcases HaxeFlixel's scale modes, which are especially handy for mobile devices where a lot of different screen sizes have to be accounted for, or desktop games that can be resized.
+ + +Showases the setTileProperties()
method of FlxTilemap
.
A port and improvement of the Split Screen demo by Philippe Mongeau featured on flixel.org/features.
+One of the new features in Flixel is the introduction of a flexible and powerful camera class called (unsurprisingly) FlxCamera. By default, a new Flixel game project starts with one camera that is the same size as the Flash Player window, which can be referenced at FlxG.camera. You can replace that camera or add additional cameras to create effects like "split screen" views, or "picture in picture" style displays, or even mini-maps.
+Each camera is an independent display object, with its own zoom, color tint, rotation, and scaling values. Finally, each game object maintains its own camera list, so you can easily instruct certain objects to only display on certain cameras. Adventurous game makers can also check out the more complex Mode source code for more ways to use cameras in-game.
+ + +This demo showcases how to load TexturePacker data with a FlxSprite.
+ + +A port and improvement of the Tilemap demo by Tim Plummer featured on flixel.org/features.
+Flixel's FlxTilemap class was inspired by old video games, in which the environment was constructed using a grid of square "tiles". Each grid cell gets a number, or index, which refers to a particular square graphic. That tile graphic is then placed at the appropriate grid number. Tilemaps have many advantages: it is easy to figure out what tiles should be drawn on screen, what tiles an object overlaps, and what special properties each tile might have. Flixel also includes some built-in algorithms for automatically placing wall tiles and floor tiles based on a simple binary (1s and 0s) array of tile data. It is a simple system with a lot of flexibility, which makes it perfect for rapid prototyping.
+ + +This sample shows how to load a map from the Tiled Map Editor.
+Demo Controls:
+An example showcasing how tooltips can be used in flixel-ui.
+ + +A showcase of the transition effects possible with the flixel.addons.effect.transition
package.
The Turn Based RPG game from this tutorial by SeiferTim.
+ + +BSPMapGen
+ + + + + + + +BlendModeShaders
+ + + + + + + +Breakout
+ + + + + + + +Calculator
+ + + + + + + +CollisionAndGrouping
+ + + + + + + +Colors
+ + + + + + + +Cursor
+ + + + + + + +DynamicShadows
+ + + + + + + +EZPlatformer
+ + + + + + + +FileBrowse
+ + + + + + + +Filters
+ + + + + + + +Flappybalt
+ + + + + + + +FlipRotationAnimationTiles
+ + + + + + + +Flixius
+ + + + + + + +FloodFill
+ + + + + + + +FlxAction
+ + + + + + + +FlxAsepriteUtils
+ + + + + + + +FlxAsyncLoop
+ + + + + + + +FlxAtlas
+ + + + + + + +FlxBitmapText
+ + + + + + + +FlxBunnyMark
+ + + + + + + +FlxCamera
+ + + + + + + +FlxCaveGenerator
+ + + + + + + +FlxClothSprite
+ + + + + + + +FlxCollisions
+ + + + + + + +FlxEffectSprite
+ + + + + + + +FlxFSM
+ + + + + + + +FlxInvaders
+ + + + + + + +FlxLightPuzzle
+ + + + + + + +FlxMouseEvent
+ + + + + + + +FlxNape
+ + + + + + + +FlxNapeTerrain
+ + + + + + + +FlxNapeTilemap
+ + + + + + + +FlxPexParser
+ + + + + + + +FlxPieDial
+ + + + + + + +FlxPongApi
+ + + + + + + +FlxRandom
+ + + + + + + +FlxScene
+ + + + + + + +FlxShape
+ + + + + + + +FlxSimplex
+ + + + + + + +FlxSkewedSprite
+ + + + + + + +FlxSnake
+ + + + + + + +FlxSound
+ + + + + + + +FlxSpine
+ + + + + + + +FlxSpriteFilters
+ + + + + + + +FlxTeroids
+ + + + + + + +FlxTextFormat
+ + + + + + + +FlxTilemapExt
+ + + + + + + +FlxTrailArea
+ + + + + + + +FlxTween
+ + + + + + + +FlxTypeText
+ + + + + + + +FrameCollections
+ + + + + + + +GamepadTest
+ + + + + + + +GridMovement
+ + + + + + + +HeatmapPathfinding
+ + + + + + + +MinimalistTD
+ + + + + + + +Mode
+ + + + + + + +MosaicEffect
+ + + + + + + +Parallax
+ + + + + + + +Particles
+ + + + + + + +Pathfinding
+ + + + + + + +PixelPerfectCollision
+ + + + + + + +ProjectJumper
+ + + + + + + +RPGInterface
+ + + + + + + +Replay
+ + + + + + + +Revenge
+ + + + + + + +Save
+ + + + + + + +ScaleModes
+ + + + + + + +SetTileProperties
+ + + + + + + +SplitScreen
+ + + + + + + +SubState
+ + + + + + + +TexturePackerAtlas
+ + + + + + + +Tilemap
+ + + + + + + +TiledEditor
+ + + + + + + +Tooltips
+ + + + + + + +Transitions
+ + + + + + + +TurnBasedRPG
+ + + + + + +HaxeFlixel is an open source 2D game engine written for use with the Open Flash Library and the Haxe Toolkit, it is completely free for personal or commercial use. It enables multi-platform development for native targets on mobile and desktop as well as Flash and HTML5 on web platforms.
+This project was founded by Alexander Hohlov, also known on Beeblerox on GitHub, who continues to be the project lead for the HaxeFlixel Organisation group. The project itself also has an active community with contributions from highly valued developers (by GitHub names) GameDevSam, impaler, Werdn, ProG4mr, Gama11, sergey-miryanov and more.
+++Haxe and OpenFL improves the longevity and opportunities for your Flixel Games.
+
HaxeFlixel is largely based on the AS3 version of Flixel written by Adam “Atomic” Saltsman. One of the major motivations for creating HaxeFlixel is overcoming the limitations of the ActionScript 3 language and the Adobe Flash and AIR runtime targets. HaxeFlixel has been able to incorporate and continues to add new language features of Haxe and incorporate exciting new runtime targets through OpenFL.
+The aim of this website is to provide HaxeFlixel with a friendly and connected web presence. It has been developed by Chris Decoster with great input from the community. It provides a place for experienced developers and beginners alike, to easily share their experiences in developing and learning how to make games. We have seen the website form a central location to share resources and improve HaxeFlixel itself.
+Everyone is highly encouraged to contribute to HaxeFlixel itself and the resources on this website.
+ +The founding member and Author of HaxeFlixel, Alexander completed the first version of HaxeFlixel during 2011, his work is the reason for the project's success.
+Chris is a generalist developer who has worked on various features and bug fixes for the engine and tools. Chris is also responsible for our logo, website and forum and is a long time member of the project.
+Sam is a formidable programmer who spends his working days doing GUI programming with AAA titles. Sam is also a passionate Indie developer and community organiser in the Baltimore area. He is responsible for many optimisations and improvements to the engine and has been a part of HaxeFlixel shortly after it began.
+Gama11 is a highly valued contributor who has taken great pride in making our codebase consistent and structured. He took the lead on the HaxeFlixel 3.x API refactor and improvements to the codebase structure general.
+Lars is veteran Flixel developer who is best known for his main Indie game Defender's Quest. Lars is also the main dev behind making the flixel-ui framework.
+Tiago is the programmer to thank for our Nape Physics support. Tiago has a flair for great demos and style and is a great multi talented developer.
+Sergey is an accomplished programmer who is a highly valued voice in discussions and planning for changes in the codebase. Sergey is a long time participator in the HaxeFlixel project.
+Players & designers care about actions (Mario jumps, Samus shoots, Captain
+Falcon turns, brakes, and accelerates), whereas computers care about
+inputs (The W key is PRESSED
, the left mouse button was JUST_RELEASED
,
+gamepad #2's analog stick is MOVED
with values x=0.4, y=-0.5
).
The FlxAction
API provides several benefits over handling every input case
+directly:
FlxAction
FlxAction
s come in two varieties: digital and analog. Digital is for on/off
+actions like "JUMP" and "SHOOT". Analog is for things that need a range of
+values, like jumping with more or less force, or moving a player or camera
+around. (FlxAction
s are triggered by attached FlxActionInput
s, described in
+the next section).
Digital actions let you do things like: if(SHOOT.check()) doShoot();
in your
+update loop rather than have a big ugly block that accounts for every input
+device you support.
Analog actions let you do things like moveCharacter(MOVE.x, MOVE.y)
or
+fireLaserBeam(LASER_INTENSITY.x)
, nicely hiding whatever devices are actually
+driving the input, and just providing you with the resulting floating point
+values.
To create a FlxAction
:
var jump = new FlxActionDigital();
+var move = new FlxActionAnalog();
+
+You can optionally pass in a name and a callback in the constructor. If you +provide a callback, it will fire whenever the action is triggered.
+If you are managing FlxAction
s yourself (as opposed to using the action
+manager), you will need to update the actions every frame, either by doing
+something like this:
function updateLoop()
+{
+ if (jump.check()) doJump();
+ if (move.check()) doMove(move.x, move.y);
+}
+
+...or this:
+function updateLoop()
+{
+ jump.update();
+ move.update();
+ if (jump.triggered) doJump();
+ if (move.triggered) doMove(move.x, move.y);
+}
+
+FlxAction.update()
will update the triggered
property, fire the callback
+if present, and update the x
and y
values (if an analog action).
FlxAction.check()
will update the triggered
property, fire the callback if present, update the x
and y
values (if an analog action), and return the
+value of triggered
.
Either is sufficient to keep the action updated. You must update each +action at least once per global update tick to ensure accurate input, but if you +accidentally update an action more than once per tick, it's okay -- an internal +safety check ensures that nothing bad happens.
+If you don't want to manually update FlxAction
s, use the FlxActionManager
,
+which will keep them updated for you.
FlxActionInput
A FlxActionInput
represents a specific input event on a specific input device,
+like "when the A button on gamepad #1 is JUST_PRESSED", which you can use to
+trigger a FlxAction
.
These come in digital and analog forms for every device that Flixel supports.
+The following are provided by default, but you can create your own by extending FlxActionInputDigital
and FlxActionInputAnalog
:
FlxActionInputDigitalKeyboard
FlxActionInputDigitalMouse
FlxActionInputDigitalMouseWheel
FlxActionInputDigitalGamepad
FlxActionInputDigitalIFlxInput
FlxActionInputAnalogMouse
FlxActionInputAnalogMouseMotion
FlxActionInputAnalogMousePosition
FlxActionInputAnalogClickAndDragMouseMotion
FlxActionInputAnalogGamepad
You can attach inputs like this:
+jump.addKey(SPACE, JUST_PRESSED);
+jump.addMouse(LEFT, JUST_PRESSED);
+jump.addGamepad(A, JUST_PRESSED, FIRST_ACTIVE);
+
+These helper functions are a shorthand for this equivalent:
+jump.add(new FlxActionInputDigitalKeyboard(SPACE, JUST_PRESSED));
+jump.add(new FlxActionInputDigitalMouse(LEFT, JUST_PRESSED));
+jump.add(new FlxActionInputDigitalGamepad(A, JUST_PRESSED, FIRST_ACTIVE);
+
+This will cause the "jump" action to trigger on the frame where any of the +following conditions is met: space bar was just pressed, left mouse button was +just clicked, or the bottom face button on any gamepad was just pressed.
+Each FlxActionInput
class has its own parameters that let you define exactly
+when the action should fire. For instance, here is the constructor for FlxActionInputDigitalKeyboard
:
public function new(Key:FlxKey, Trigger:FlxInputState)
+
+This requires you to specify a specific key, as well as an input state (PRESSED
,
+JUST_PRESSED
, RELEASED
, JUST_RELEASED
).
Now here's the constructor for FlxActionInputDigitalGamepad
, note that in
+addition to specifying the button and the trigger state, we also have to specify
+which gamepad we're listening for, since there could be more than one:
public function new(InputID:FlxGamepadInputID, Trigger:FlxInputState, GamepadID:Int = FlxInputDeviceID.FIRST_ACTIVE)
+
+Now let's take a quick look at some analog inputs.
+Here's FlxActionInputAnalogGamepad
:
public function new(InputID:FlxGamepadInputID, Trigger:FlxAnalogState, Axis:FlxAnalogAxis = EITHER, GamepadID:Int = FlxInputDeviceID.FIRST_ACTIVE)
+
+In addition to having to specify the input (left/right stick or left/right
+trigger), the trigger state, and the gamepad ID, we also have to specify which
+analog axis we care about (X
, Y
, EITHER
, BOTH
). (Note that for
+single-axis analog inputs, such as analog triggers, only the x value will change
+and the y value will always be zero).
Another example is FlxActionInputAnalogMousePosition
:
public function new(Trigger:FlxAnalogState, Axis:FlxAnalogAxis = EITHER)
+
+Since there's only ever one mouse, we don't need to specify the device, and we don't
+need to specify a button since we just want the position. Whenever this action
+updates the x
and y
values will match the mouse position.
FlxActionSet
A FlxActionSet
is little more than a glorified array of FlxAction
s. There's
+little reason to use them directly unless you are using the FlxActionManager
,
+but they can still be a convenient way to call update()
on all your actions
+at once.
FlxActionManager
FlxActionManager
lets you manage multiple actions without having to update
+them manually, and also lets you control action sets. Action sets are groups of
+actions that can be selectively activated for specific input devices at specific times, which is great for local multiplayer games, games with complex input, and
+games using the Steam Input API.
FlxActionManager
is not initialized in Flixel by default, you have to add it yourself in your initialization code:
var actionManager = new FlxActionManager();
+FlxG.inputs.add(actionManager);
+
+Once the action manager has been set up, you can simply add actions to it, and +it will ensure that all your actions are kept up to date:
+//add actions one by one:
+actionManager.addAction(jump);
+actionManager.addAction(shoot);
+
+//add several actions at once:
+actionManager.addActions([action1, action2, action3, action4, action5]);
+
+Then in your update loop you can simply check the triggered
property, or just
+wait for callbacks to fire, if you've set any.
function updateLoop()
+{
+ if (jump.triggered) doJump();
+ if (shoot.triggered) doShoot();
+}
+
+What's actually happening when you call addAction
or addActions
is that the
+manager is asking for both actions and the action set you want to add them to:
public function addAction(Action:FlxAction, ActionSet:Int = 0):Bool
+
+If you don't provide an action set, it assumes you want to add them to the first +one (index 0). And if you haven't defined any action sets, it will create one +for you at index 0, name it "default", and immediately activate it for all +devices.
+NOTE:
+By default, when you change FlxState
s, the default action set, including all referenced actions and inputs, will be cleared out and destroyed. You can change
+this behavior by modifying the FlxActionManager.resetOnStateSwitch
policy.
If you're not juggling multiple action sets, you will probably never need to +worry about any of this -- just add new actions at the start of every state. +We like to explain what's going on under the hood just in case you encounter unexpected behavior.
+Only ONE action set is considered active at a given time for any given device, +but multiple devices can be subscribed to the same action set.
+For instance, in an asymetrical co-op game where one person drives a tank +and the other mans the turret, gamepad #1 could use action set "drive" and +gamepad #2 could use action set "gunner". The same could go for the mouse, +the keyboard, etc. And in a single-player game you might want to just change +the action set of ALL input devices every time you switch to a different +screen, such as a menu.
+FlxActionManager
lets you:
To create and add an action set, do something like:
+//where up, down, left, right, select are digital actions
+var set = new FlxActionSet("menu", [up, down, left, right, select]);
+var menuSetIndex = actionManager.addSet(set);
+
+Note that addSet
returns the action set's index. All operations on action sets
+require the action set's index (an Int
), not its name (a String
). This is
+for performance reasons. If you forget to store the action set, or otherwise
+lose track of an action set's index, you can query it at any time by passing the
+set's name to getSetIndex()
. Just be sure not to do this repeatedly in
+frequently called loops.
If you are using the steamwrap library, FlxActionManager
gains the ability
+to automatically create action sets from a steamwrap object derived from the
+master vdf game actions file that Steam makes you set up. You must then ACTIVATE
+one of those action sets for any connected steam controllers, which will
+automatically attach the proper steam action inputs to the actions in the set.
+You can also add as many regular FlxActionInput
s as you like to any actions in
+the sets.
var config = steamwrap.data.ControllerConfig.fromVDF(myVDF);
+actionManager.initSteam(config, digitalCallback, analogCallback);
+
+NOTE:
+If you are using the Steam Input API and/or a Steam Controller, you MUST use FlxActionManager
in order to properly process Steam's API via FlxAction
s.
+The only other alternative is to call the steamwrap functions directly.
FlxActionManager
can generate a JSON string representation of your action sets via exportToJson()
You can also initialize your action sets by calling
+initFromJson()
, feeding in the parsed object representation of the same format.
The format is:
+typedef ActionSetJsonArray =
+{
+ @:optional var actionSets:Array<ActionSetJSON>;
+}
+
+typedef ActionSetJson =
+{
+ @:optional var name:String;
+ @:optional var analogActions:Array<String>;
+ @:optional var digitalActions:Array<String>;
+}
+
+Which would look something like this in practice:
+{
+ "actionSets" : [
+ {
+ "name" : "SomeSet",
+ "analogActions" : [
+ "some_analog_action_1",
+ "some_analog_action_2"
+ ],
+ "digitalActions" : [
+ "some_digital_action_1",
+ "some_digital_action_2"
+ ]
+ },
+ {
+ "name" : "AnotherSet",
+ "analogActions" : [
+ "another_analog_action_1",
+ "another_analog_action_2"
+ ],
+ "digitalActions" : [
+ "another_digital_action_1",
+ "another_digital_action_2"
+ ]
+ }
+ ]
+}
+
+
+
+ The Android target makes use of a chain of frameworks to compile your native Android game from Haxe code. OpenFL uses the Hxcpp and the Android NDK specifically so no virtual machine is involved.
+To set up android, run lime setup android
after installing HaxeFlixel. You can choose to download necessary components (eg. Android SDK and NDK) or use existing installations.
The Haxe compiler uses its cpp
target to compile your Haxe
code for the LibSDL OpenGL library so that the Android NDK can then use this "native-code" for your Android game. You can read more about the Android NDK from Google here, however this process is completely automated by OpenFL. Android is part of the cpp group of targets and when developers mention cpp
the topic may be relevant to HaxeFlixel Android.
With OpenFL using native-code and OpenGL with LibSDL, the rendering methods are different to where Flixel started with Flash. Android uses GPU accelerated Texture Batching for the best possible performance on mobile devices.
+#if cpp
+//your android code
+#end
+
+#if android
+//your android code
+#end
+
+#if mobile
+//your android code
+#end
+
Mobile platforms can use a window width and height of 0, which is a special value that uses the full resolution of the current display.
+<window width="0" height="0" background="#FFFFFF" fps="60" />
+
OpenFL also exposes the following specific settings for the Android target:
+<android target-sdk-version="17" />
+<window hardware="true" allow-shaders="true" require-shaders="true" if="cpp"/>
+<window vsync="true" antialiasing="4" if="cpp" />
+<window orientation="portrait" /> || <window orientation="landscape" if="cpp"/>
+
Custom PNG icons: (Check Iconography / Android Developers for more info)
+<icon path="36.png" size="36" if="android" />
+<icon path="48.png" size="48" if="android" />
+<icon path="72.png" size="72" if="android" />
+<icon path="96.png" size="96" if="android" />
+
Visual Studio Code, FlashDevelop and IntelliJ IDEA support Android compilation through their GUI.
+The basic command to compile and test Android:
+lime test android
+
Run this command from the root folder of your project; the default project.xml will be used automatically. For the test command to run on your device you should have it connected with ADB working correctly.
+If you want to use the Android simulator, add -simulator
when running/testing. Be sure your virtual device is API >=15 and has GPU enabled.
lime test android -simulator
+
+
+ package;
+
+import flixel.FlxSprite;
+import flixel.FlxG;
+
+class MySprite extends FlxSprite
+{
+ public function new()
+ {
+ super();
+ }
+
+ override public function update(elapsed:Float):Void
+ {
+ super.update(elapsed);
+ }
+}
+
+package;
+
+import flixel.FlxState;
+import flixel.FlxG;
+
+class MyState extends FlxState
+{
+ override public function create():Void
+ {
+ super.create();
+ }
+
+ override public function update(elapsed:Float):Void
+ {
+ super.update(elapsed);
+ }
+}
+
+FlxG.switchState(new MyState());
+
+loadGraphic("assets/my_sprite.png");
+
+// OR dynamically create a rect
+makeGraphic(100, 100, 0xFFFFFFFF); // width, height, color (AARRGGBB hexadecimal)
+
+// to update bounding box by default (positioned on top left corner of the sprite)
+updateHitbox(); // or offset.set(10, 5); to shift bounding box 10 pixels horizontally and 5 pixels vertically
+
+import flixel.text.FlxText;
+import flixel.util.FlxColor;
+
+myText = new FlxText(0, 0, 500); // x, y, width
+myText.text = "Hello World";
+myText.setFormat("assets/font.ttf", 20, FlxColor.WHITE, CENTER);
+myText.setBorderStyle(OUTLINE, FlxColor.RED, 1);
+
+import flixel.ui.FlxButton;
+
+myButton = new FlxButton(0, 0, "Label", myCallback);
+
+// Custom graphics (sprite sheet with 3 frames for normal / highlight / pressed)
+myButton.loadGraphic("assets/custom.png", true, width, height);
+
+function myCallback():Void
+{
+}
+
+FlxText
, use setFormat()
and setBorderStyle()
to customise.With the stock Project.xml
, simply place them in your project's assets/music
and assets/sounds
subfolders and they're ready to use.
Sound effects are usually in WAV format (44.1 kHz source).
+Music must be in MP3 format (44.1 kHz source) for Flash, and OGG for everything else. To support both Flash and non-Flash platforms without bundling both formats in your output, you can replace the stock <assets>
tag in your Project.xml
with this:
<assets path="assets" exclude="*.ogg" if="flash"/>
+<assets path="assets" exclude="*.mp3" unless="flash"/>
+
+Play in your code:
+// Play sound effect using AssetPaths
+FlxG.sound.play(AssetPaths.mySound__wav);
+// Play sound effect without AssetPaths
+FlxG.sound.play("assets/sounds/mySound.wav");
+
+// Loop music, Flash only
+FlxG.sound.playMusic(AssetPaths.myMusic__mp3);
+// Loop music, non-Flash only
+FlxG.sound.playMusic(AssetPaths.myMusic__ogg);
+// Loop music, Flash or non (getSound() adds .mp3 on Flash and .ogg otherwise)
+FlxG.sound.playMusic(FlxAssets.getSound("assets/music/myMusic"));
+
+// 'A' key
+if (FlxG.keys.justPressed.A) {}
+if (FlxG.keys.pressed.A) {}
+if (FlxG.keys.justReleased.A) {}
+
+// Checking multiple keys:
+if (FlxG.keys.anyPressed([RIGHT, D])) {}
+
+ANY
A
...Z
UP
DOWN
LEFT
RIGHT
SPACE
ENTER
ESCAPE
ZERO
ONE
TWO
THREE
...NINE
F1
...F12
ALT
+BACKSLASH
+BACKSPACE
+CAPSLOCK
+CONTROL
+DELETE
+HOME
+INSERT
+QUOTE
+PERIOD
+PLUS
+MINUS
+PAGEUP
+PAGEDOWN
+RBRACKET
+GRAVEACCENT
+TAB
+SLASH
+SEMICOLON
NUMPADZERO
NUMPADONE
NUMPADTWO
...NUMPADNINE
if (FlxG.mouse.pressed) {}
+if (FlxG.mouse.justPressed) {}
+if (FlxG.mouse.justReleased) {}
+
+// Relative to world space
+FlxG.mouse.x;
+FlxG.mouse.y;
+
+// Relative to screen
+FlxG.mouse.screenX;
+FlxG.mouse.screenY;
+
+Current "delta" value of mouse wheel. If the wheel was just scrolled up, it will have a positive value. If it was just scrolled down, it will have a negative value. If it wasn't just scroll this frame, it will be 0.
+FlxG.mouse.wheel;
+
+var clickableSprite:FlxSprite;
+
+// ...
+
+// register plugin in PlayState.create()
+FlxMouseEventManager.init();
+
+// ...
+
+// register callbacks
+var pixelPerfect = false;
+FlxMouseEventManager.add(clickableSprite, mousePressedCallback, mouseReleasedCallback, null, null, false, true, pixelPerfect, [FlxMouseButtonID.LEFT, FlxMouseButtonID.RIGHT]);
+
+// ...
+
+function mousePressedCallback(sprite:FlxSprite)
+{
+ if (FlxG.mouse.justPressed)
+ {
+ // left button was pressed
+ }
+ else if (FlxG.mouse.justPressedRight)
+ {
+ // right button was pressed
+ }
+}
+
+function mouseReleasedCallback(sprite:FlxSprite)
+{
+}
+
+for (touch in FlxG.touches.list)
+{
+ if (touch.justPressed) {}
+ if (touch.pressed) {}
+ if (touch.justReleased) {}
+}
+
+// Relative to world space
+touch.x;
+touch.y;
+
+// Relative to screen
+touch.screenX;
+touch.screenY;
+
+touchPointID
: The unique ID of this touch.
overlaps(objectOrGroup)
: Checks for overlap between this touch and another FlxObject
or FlxGroup
.
"Swipes" from both mouse and touch input that have just been released:
+for (swipe in FlxG.swipes)
+{
+ // swipe.startPosition (FlxPoint)
+ // swipe.endPosition (FlxPoint)
+
+ // swipe.id (Int)
+
+ // swipe.distance (Float)
+ // swipe.angle (Float)
+ // swipe.duration (Float)
+}
+
+import flixel.util.FlxSignal;
+
+// for signals that don't need data, use FlxSignal
+var signal = new FlxSignal();
+// for signals that need data, use FlxTypedSignal with the correct function type
+var stringSignal = new FlxTypedSignal<String->Void>();
+
+Note: FlxSignal
is nothing but a convenient shortcut for FlxTypedSignal<Void->Void>
signal.add(voidCallback); // type must be Void->Void
+stringSignal.add(stringCallback); // type must be String->Void
+
+function voidCallback()
+{
+ trace("Hello");
+}
+
+function stringCallback(text:String)
+{
+ trace(text);
+}
+
+// this will print "Hello World"
+signal.dispatch();
+stringSignal.dispatch("World");
+
+You can have up to 4 parameters in your signal:
+var collisionNotify = new FlxTypedSignal<FlxObject->FlxObject->Bool->Bool->Void>();
+collisionNotify.add(collisionCallback);
+
+function collisionCallback(source:FlxObject, target:FlxObject, shouldKillSource:Bool, shouldKillTarget:Bool):Void (...)
+
+import flixel.util.FlxTimer;
+
+// time (seconds), callback, loops
+new FlxTimer().start(10.0, myCallback, 3);
+
+function myCallback(timer:FlxTimer):Void
+{
+}
+
+loops
to 0
results in an endless loop.reset(?NewTime)
restarts the timer, optionally with a new duration.cancel()
stops the timer and removes it from the timer manager.// (Int) between 0 and 10
+FlxG.random.int(0, 10);
+
+// (Float) between 0.0 and 10.0
+FlxG.random.float(0.0, 10.0);
+
+// (Bool) Chance by percent
+FlxG.random.bool(50); // 50% chance to return 'true'
+FlxG.random.bool(10); // 10% chance to return 'true'
+
+Check the demo to visualize all FlxTween
types.
import flixel.tweens.FlxTween;
+import flixel.tweens.FlxEase;
+
+// Moves sprite to position (100, 200) in 3 seconds
+FlxTween.tween(sprite, {x: 100, y: 200}, 3.0, {ease: FlxEase.quadInOut, complete: myCallback});
+
+function myCallback(tween:FlxTween):Void
+{
+}
+
+{ease: FlxEase.quadInOut}
+
+{complete: callbackFunction}
+
+function callbackFunction(tween:FlxTween):Void
+{
+}
+
+{type: FlxTweenType.PINGPONG}
+
+{loopDelay: 1.0} // 1 second
+
+{startDelay: 2.0} // 2 seconds
+
+Check the demo to visualize all FlxEase
types.
backIn
, backInOut
, backOut
bounceIn
, bounceInOut
, bounceOut
circIn
, circInOut
, circOut
cubeIn
, cubeInOut
, cubeOut
elasticIn
, elasticInOut
, elasticOut
expoIn
, expoInOut
, expoOut
quadIn
, quadInOut
, quadOut
quartIn
, quartInOut
, quartOut
quintIn
, quintInOut
, quintOut
sineIn
, sineInOut
, sineOut
FlxGroup
is a shortcut for FlxTypedGroup<FlxBasic>
. Use FlxTypedGroup<MyOwnClass>
if you need to access your own variables and functions when iterating over the container.
for (member in myGroup)
+{
+ member.x += 10;
+ member.mySpecificFunction(); // myGroup = FlxTypedGroup<MyOwnClass>
+}
+
+FlxG.overlap(objectOrGroup1, objectOrGroup2, myCallback);
+
+function myCallback(object1:FlxObject, object2:FlxObject):Void
+{
+}
+
+Or use FlxG.collide()
which calls FlxG.overlap()
and presets the processCallback
parameter to FlxObject.separate()
.
// collision won't work outside the bounds, and by default they are only size of one screen
+FlxG.worldBounds.set(tilemap.x, tilemap.y, tilemap.width, tilemap.height);
+
+// sets the touching flags
+FlxG.collide(player, level);
+
+if (player.isTouching(DOWN))
+{
+ // player stands on the ground and can jump
+}
+
+// will reset touching flags when called
+super.update(elapsed);
+
+// after setting the sprite's new position
+setPosition(10, 100);
+// don't forget to update 'last' variable if you don't want overlap callbacks for objects between old and new positions of the sprite
+last.set(x, y);
+
+var overlapping = FlxG.pixelPerfectOverlap(sprite1, sprite2);
+
+Dynamically draw: circle, ellipse, line, polygon, triangle, rect, round rect and rect complex.
+using flixel.util.FlxSpriteUtil;
+
+Haxe docs about the using
keyword: haxe.org/manual/lf-static-extension.html.
var canvas = new FlxSprite();
+canvas.makeGraphic(FlxG.width, FlxG.height, FlxColor.TRANSPARENT, true);
+add(canvas);
+
+The last argument of makeGraphic()
is Unique
, whether the graphic should be an unique instance in the graphics cache, if you create multiple graphics like this, set it to true
to avoid conflicts.
var lineStyle:LineStyle = {color: FlxColor.RED, thickness: 1};
+var drawStyle:DrawStyle = {smoothing: true};
+
+// Circle
+canvas.drawCircle(x, y, radius, color, lineStyle, drawStyle);
+
+// Ellipse
+canvas.drawEllipse(x, y, width, height, color, lineStyle, drawStyle);
+
+// Line
+canvas.drawLine(startX, startY, endX, endY, lineStyle);
+
+// Polygon
+var vertices = new Array<FlxPoint>();
+vertices[0] = new FlxPoint(0, 0);
+vertices[1] = new FlxPoint(100, 0);
+vertices[2] = new FlxPoint(100, 300);
+vertices[3] = new FlxPoint(0, 100);
+canvas.drawPolygon(vertices, color, lineStyle, drawStyle);
+
+// Triangle
+canvas.drawTriangle(x, y, height, color, lineStyle, drawStyle);
+
+// Rect
+canvas.drawRect(x, y, width, height, color, lineStyle, drawStyle);
+
+// Round Rect
+canvas.drawRoundRect(x, y, width, height, ellipseWidth, ellipseHeight, color, lineStyle, drawStyle);
+
+// Rect Complex
+canvas.drawRoundRectComplex(x, y, width, height, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, color, lineStyle, drawStyle);
+
+Use canvas.fill(FlxColor.TRANSPARENT);
to clear the canvas.
// prevents the sprite to scroll with the camera
+scrollFactor.set(0, 0);
+
+// create a new camera for the HUD
+uiCamera = new FlxCamera(0, 0, screenWidth, screenHeight);
+uiCamera.bgColor = FlxColor.TRANSPARENT;
+
+// add camera to list and set 'DefaultDrawTarget' to false
+FlxG.cameras.add(uiCamera, false);
+
+// add element to the camera
+hudElement.cameras = [uiCamera];
+
+FlxG.camera.bgColor = 0xff626a71;
+FlxG.camera.zoom = 0.5; // zoom only on the default camera
+
+Press ~ key
to open it during runtime, or open by code with FlxG.debugger.visible = true
.
// Log
+FlxG.log.add("My var: " + myVar);
+// or
+FlxG.log.redirectTraces = true;
+trace("My var: ", myVar);
+
+// Watch
+FlxG.watch.add(object, "property");
+
+// Add world space mouse position to watch list
+FlxG.watch.addMouse();
+
+// Create a tracker window for player (of class Player) showing "x", "y" and custom fields "jumping" and "ladder"
+FlxG.debugger.addTrackerProfile(new TrackerProfile(Player, ["x", "y", "jumping", "ladder"], []));
+FlxG.debugger.track(player, "Hero");
+
+FlxG.mouse.visible = false;
+
+acceleration.y = 600;
+
+// sort by Y for top-down game
+group.sort(FlxSort.byY, FlxSort.ASCENDING);
+
+// sort with custom function (here: by Z)
+var group = new FlxTypedGroup<ZSprite>();
+group.sort(
+ function(order:Int, sprite1:ZSprite, sprite2:ZSprite):Int
+ {
+ return FlxSort.byValues(order, sprite1.z, sprite2.z);
+ },
+ FlxSort.ASCENDING
+);
+
+// get from FlxPoint pool
+var tileSize = FlxPoint.get(16, 16);
+
+var actionTileset = FlxTileFrames.fromGraphic(FlxG.bitmap.add("assets/images/ui/actions.png"), tileSize);
+
+// release it back in pool to reuse
+tileSize.put();
+
+
+
+ Contributing code to HaxeFlixel is done all through the official git repositories on GitHub.
+All repositories except the website and documentation use a dev
branch which is the main entry point for features and bugfixes to accepted into the codebase. When the code is tested by the community it is then merged into master and released on haxelib.
To clarify: the dev
branch on flixel-addons and flixel-demos is only compatible with the dev
branch of flixel.
If you are making changes to the codebase that could include breaking changes or a new API, we make use of the pull request feature and suggest developers use a feature branch model. Feature branches are simply new branches with your code that are named with a title relating to your code.
+Before a major feature is merged into core our general workflow is to get approval from a core-contributor with push access.
+Developers do have limited time so keep in mind some simple steps to get your pull request accepted:
+Everything about the technology stack of HaxeFlixel is open-source so you can contribute directly to the language, compiler and upstream libraries:
+If you are wanting to contribute code, please review the style guide.
+ + +This page contains a few notes on HaxeFlixel's code style. Note that we use haxe-formatter to enforce consistent whitespace usage, bracket placement etc, so this page only focuses on naming conventions and other good practices not covered by an auto-formatter.
+function shootEnemy(target:Enemy, bullet:BulletType):Void
+
+...is easier to read than
+function shootAtASpecificEnemyWithABulletTypeOf(target:Enemy, bullet:BulletType):Void
+
+...but still gets the idea of what this function does across. The second example has a lot of "noise"-words that don't provide any additional value. The following function name would still be acceptable, more accurate than example 1 even:
+function shootBulletAtEnemy(target:Enemy, bullet:BulletType):Void
+
+However, function names should also not be too short - you should be able to roughly know what a function does simply by reading its name - reading its description, if existent, should ideally not be necessary.
+An example for a bad / too simple name would be shoot()
instead of shootEnemy()
.
Use lowerCamelCases for function parameters and use this
to explicitly reference to class members (only when necessary):
function translate(words:String, bableFish:BableFish):Void
+{
+ this.words = words;
+}
+
+Instead of:
+function translate(Words:String, BableFish:BableFish):Void
+{
+ words = Words;
+}
+
+In the core of HaxeFlixel, a lot of method parameters are capitalized (not preferred in Haxe). This is mostly legacy from the AS3 Flixel's code style and hard to change them all. However, newly written functions should follow the lowerCamelCase
style.
The Haxe compiler does not require you to declare the type of a variable (read more).
+//This is unnecessary
+var name:String = "Merlin";
+var number:Int = 32;
+
+//This is preferred
+var name = "Merlin";
+var number = 32;
+
+In some contexts however, doing so improves the readability of the code, for example because it's not immediately obvious what return type a function has:
+var mystery:Answer = createAnAnswer();
+
+The original AS3 Flixel codebase ordered keywords like static public function
.
+HaxeFlixel has changed this convention to be more like every other (Haxe) library, the order of importance is as follows:
override
public
/ private
static
inline
So static public function
is changed to public static function
. This also applies to variables eg public static var
.
The following rules apply, sorted by priority:
+static
fields before instance fieldspublic
fields before private
fieldsComments should only be used when you can't find a way to express the same thing in code. They tend to add noise to the code base and rarely get updated when the code changes, which results in misinformation and inaccurate comments.
+Take the following comment section for example, it does not provide any additional value beyond what the code and the class / parameter names already tell you (one of the reasons why choosing good names is so important!). It should thus be removed completely.
+/**
+ * Sets the position.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ */
+public function setPosition(x:Int, y:Int):Void
+
+All type names should be prefixed by Flx
, e.g.:
FlxBasic
FlxObject
FlxSprite
Even though this is widely regarded as bad style, it still makes sense to follow this convention since it is so deeply ingrained into the flixel workflow and doing otherwise would be very inconsistent and confusing.
+This convention does not come without its advantages, for example you can easily tell which classes belong to the engine and which ones don't when writing a game.
+Under the hood types that HaxeFlixel devs will likely never see or use should not have the Flx
prefix, this will serve as an indicator that they are off the beaten path.
The names of enum values are capitalized. The same convention applies to abstract enums.
+enum FlxCameraFollowStyle
+{
+ LOCKON;
+ PLATFORMER;
+ TOPDOWN;
+ TOPDOWN_TIGHT;
+ SCREEN_BY_SCREEN;
+ NO_DEAD_ZONE;
+}
+
+Unlike in most other languages with object-oriented syntax, everything in Haxe is an expression. This extends to switch-case
, meaning that it evaluates to a value. This value can be assigned to a variable, passed to a function or simply returned by a function:
function getColor(color:Color):Int
+{
+ return switch (color)
+ {
+ case Color.RED: 0xff0000;
+ case Color.BLUE: 0x0000ff;
+ case Color.GREEN: 0x00ff00;
+ }
+}
+
+This is preferable to how this code would look like in a lot of other languages, for example C#:
+int getColor(Color color) {
+ switch (color) {
+ case Color.RED:
+ return 0xff0000;
+ case Color.BLUE:
+ return 0x0000ff;
+ case Color.GREEN:
+ return 0x00ff00;
+ }
+ return 0x000000;
+}
+
+
+
+ While we try our best to make good documentation and a good example tutorial, we can only do so much! +So this will be a big list to various external Community Tutorials made by other people!
+HaxeFlixel is a collaborative project from contributors all over the world.
+If you need help or just want to chat , HaxeFlixel has a...
+Other HaxeFlixel-related pages:
+ + + +The Haxe compiler provides a robust solution for cross-platform development where you can use operators to define what targets receive your code. This functionality is invaluable for HaxeFlixel since we are targeting native mobile / desktop and web targets, all with different capabilities.
+Conditional Compilation in the Haxe Manual
+A basic example may include logic like this:
+#if desktop
+
+// desktop only code
+
+#elseif mobile
+
+// mobile only code
+
+#end
+
+Conditionals relevant to your HaxeFlixel games may include:
+mobile
, desktop
, web
ios
, android
, windows
, mac
, linux
, html5
flash
, cpp
, neko
, js
Multiple targets can be used together:
+#if (mac || linux || android)
+
+// code specific for these platforms
+
+#end
+
+To define your own it is as easy as adding to your Project.xml
:
<haxedef name="magic" />
+
+Now this will work:
+#if magic
+// Create a dragon
+#end
+
+Since Haxe lets you use some logic with the conditionals you can enable something just for mobile, as in:
+<haxedef name="magic" if="mobile"/>
+
+
+
+ Now we have a pretty complete, working game! It's not too complex, but it should give you a full, well-rounded idea of how to make your own games in HaxeFlixel. I really hope you've enjoyed this tutorial, and take the time to play around with the code some more to try expanding the game even further. You could add more levels, add stairway objects to move between them, add more enemy types, and collectibles, and upgrades… the list goes on.
+As mentioned previously, you can grab the complete source code for the game here.
+The power of HaxeFlixel comes from it's balance of simplicity and versatility. With only a few lines of code you can do almost anything you can think of.
+If you want to learn more about using HaxeFlixel, take a look at the documentation, and API for lots more in-depth information.
+I'd like to thank Jens Fischer, Justo Delgado Baudí, the STL IGDA, and the HaxeFlixel Dev Community for helping me get this tutorial finished - a lot of editing and suggestions for improvement came from these people.
+If you enjoyed this tutorial, if it helped you in some way, or if you have suggestions, I always appreciate feedback. You can let me know at seifertim@gmail.com or on twitter: @seifertim.
+Thanks, and happy coding!
+ + +We welcome new contributors to this project. +Being open source and followed by people all over the world, you can help in many ways.
+HaxeFlixel is a an open-source project with humble beginnings +sparked from a passion for the technology and game development. Everyone working on this project has come together +to create a friendly and flexible approach so that this library and community is not only enjoyable, but a robust solution for cross-platform 2D games.
+We are built on code contributions, which we gladly welcome - see the code contributions page.
+Help spread the word! What you can do to help:
+HaxeFlixel is currently accepting donations via its Patreon page.
+Please read this blog post to understand how the funds will be distributed.
+@gamedevsam is the current treasurer for HaxeFlixel, contact him if you have any questions or suggestions regarding the use of donated funds.
+ + +Now we want to make sure that everything is installed properly - and we want to set up the framework for our game. So we're going to make a new project, open it with VSCode and test that it builds and runs.
+In the command prompt, enter:
+ flixel tpl -n "TurnBasedRPG" -ide vscode
+
+ This will create a new Flixel project using the blank template in the directory TurnBasedRPG
(as well as naming the project TurnBasedRPG
).
Depending on how you've configured flixel-tools and whether the code
command is available, flixel tpl
might already have opened a VSCode window with the right folder automatically.
If not, simply open the TurnBasedRPG
folder with File > Open Folder...
.
Over on the left side of the screen, you should see your project. This will contain all the files that the template generated for us.
++
Next, let's make sure that our project builds and runs properly. By default, a fresh, new Flixel template project will launch the game in the PlayState
. So go ahead and double-click to open that file from the project list.
+ This file is a very basic (and empty) example of a FlxState
subclass. You can think of a FlxState
a little bit like a 'scene'. When your game is running, one - and only one - state will be the active state. Anything 'added' to that state will be visible and accessible by the player, and, for the most part, will be segregated from other states in your game.
They are great for having different sections of your game that are sort of in a bubble from one another, such as a MenuState
(which we will add later) and the PlayState
(where the game is actually played).
You'll see that, at least for now, the PlayState
only has two functions in it: create()
and update()
. When a state is loaded, its create()
function is called. This is where you want to initialize all of the things in your state.
update()
is where all the real magic happens - it is called every 'frame' in your game (by default 60 times per second). When a FlxState
's update()
is called, it will call update()
on all of the objects that have been added to it. This is basically how everything in your game actually 'happens'. We will get to play with this more later on.
+ For now, we JUST want to see that the whole thing works, so, we're going to add a simple FlxText
object to this state.
In create()
, right before the line that says super.create();
, type out the following two lines:
var text = new FlxText(10, 10, 100, "Hello, World!");
+ add(text);
+
+ While you're typing, code completion should pop up and suggest FlxText
.
+
Here, press Enter
or simply type a (
- notice how the following lines has automatically been added to your imports at the top of the file:
import flixel.text.FlxText;
+
+ Then save your changes (or simply enable auto-save via File > Auto Save
).
The moment of truth! Go down to the status bar in VSCode and verify that the HTML5 target is selected in the lower left:
++
Then run the "build task" (or press the keyboard shortcut for it)!
++
After a short moment, your default browser should pop up with a HaxeFlixel splash screen and then the text we just added:
++
It worked! You've built and ran your first HaxeFlixel project!
+At this point, you could try building it for Neko, Windows and Android to make sure they all work as well. If you run into problems with any of those, double-check your code, check out the OpenFL installation information, and if that doesn't help, get in touch with the community.
+This is only a small step towards bigger and better things! In the next part, we'll actually define the game we're going to be building, and start putting it together!
+Now it's time to make some maps for the player to move around in! To do this, we're going to use a tool called Ogmo Editor. Ogmo is a free tilemap editor that works very nicely with HaxeFlixel. For this part of the tutorial, we're just going to use a simple 2-tile tilesheet with a tile for walls and a tile for floors.
+You can make your own, with 16x16 pixel tiles, or use this one:
+ +(Note: the first tile should be empty!)
+Download and install Ogmo Editor 3, then launch it.
+Click on New Project
and navigate to assets/data
- save the project as turnBasedRPG.ogmo
.
On the General
tab, you can rename your project, set the default level sizes, and more. Set it up like this:
+
On the Layers
tab, make an Entity Layer
called entities
and a Tile Layer
called walls
:
+
On the Entities
tab, make an entity called player
:
+
Finally, on the Tilesets
tab, make a new tileset called tiles
and load the tiles from earlier:
+
When you're done, click on Save
and you'll be brought to this screen:
+
Make sure you're on the walls
layer, and using the tools at the top, draw out a simple map. Make sure it's completely encircled by walls (so the player can't wander off the map), and make the insides filled with floor tiles. You should end up with something like this:
+
Now, switch over to the 'entities' layer, and place your player entity somewhere in one of your rooms:
++
Hit Ctrl+S (Cmd+S on Mac), and save this level as room-001.json
in assets/data
.
We're done with Ogmo for now, so save all your changes and exit.
+In the next part, we will learn how to load the newly created tilemap into our game.
+ + +The interaction tool (enabled by the button in the debugger overlay) allows you to change game elements, e.g. move sprites, while the game is still running.
+It is highly recommended, however, that you pause the game using VCR's pause button before using the interaction tool. You can always resume the game by clicking VCR's play button after you are done with the interaction. If the game is not paused during the interaction, objects affected by acceleration, for instance, will continue to move, making your work harder.
+When the interaction is enabled, a toolbar containing a few related tools is displayed on the left side of the screen. Below is a description of each tool.
+The pointer tool allows you to select game elements. In order to use it, simply click elements on the screen:
+ +Currently only elements that extend FlxSprite
can be selected (which exclude tiles of a tilemap, for instance). Elements that belong to your selection will be highlighted in red. If you click an empty space, the selection will be discarded.
You can add/remove elements to/from your existing selection by holding CTRL
when you click elements. It is also possible to add/remove a group of elements by clicking and dragging the mouse cursor to create a selection area:
If you press the DELETE
key when elements are selected, the debugger will invoke the kill()
method of each element in that selection. You can also press DELETE
while holding SHIFT
to invoke the kill()
method and additionally remove the elements from memory, i.e. elements will be removed from Flixel's display list.
The mover tool allows you to move game objects that were selected by the pointer tool. In order to use this functionality, select any element(s) using the pointer tool, then pick the mover tool and click-and-drag anywhere on the screen:
+ +The selected elements will follow the movement of the mouse cursor until you release the mouse button.
+The mover tool is also activated while the SHIFT
key is kept pressed, so you can move selected elements at any time, even when any other tool is active. After performing a selection, hold SHIFT
then click-and-drag anywhere on the screen to move the selected element(s):
When you stop pressing SHIFT
, the element(s) will stop moving and your previously active tool will become effective again.
The transform tool allows you to resize and rotate a selected game object. In order to use this functionality, select a single element using the pointer tool, then pick the transform tool and click-and-drag any of the markers around the object:
+ +The circular marker at the top-left corner of the selected object can be used to rotate it, while the squared markers can be used to resize it.
+ + +Flixel comes with a fairly powerful debugging overlay. You can open it with one of the default toggle keys (F2
, ` and \ with a QWERTY keyboard layout). Note that these are configurable via FlxG.debugger.toggleKeys
. Alternatively, you can do the same in code via the FlxG.debugger.visible
flag.
Note that the debugger does not exist when compiling with FLX_NO_DEBUG
. With the default Project.xml
, this is the case in release mode. On the command line, use the -debug
flag to build in debug mode instead (e.g. lime test neko -debug
).
FlxG.debugger.drawDebug
can be enabled to display the hitboxes of every FlxObject
added to the state (alternatively, press the cube button in the upper right corner of the debugger).
The hitboxes are color-coded based on the collision properties. For FlxObject
and FlxSprite
this means:
allowCollisions == NONE
immovable
objectsThe color is customizable via the debugBoundingBoxColor
property.
The behavior of tiles in FlxTilemap
is slightly different:
allowCollisions == NONE
allowCollisions == ANY
allowCollisions
The log window is used to display traces and can be accessed via FlxG.log
. You can also redirect any trace()
-calls to it with FlxG.log.redirectTraces = true;
. Using it is mostly a matter of preference, some people prefer to have their traces displayed in their IDE of choice (FlashDevelop's output panel for example).
Some flixel-internal errors will also be output to the log window.
+It's possible to customize things like text color, size, style or add a prefix by using log styles. FlxG.log.warn()
, error()
and notice()
use pre-defined log styles.
Here's an example of how you could differentiate each trace by the player from others by adding a [Player]
prefix and printing it in green:
var playerAction = new LogStyle("[Player]", "00FF40");
+FlxG.log.advanced(" Shoot", playerAction);
+
+It's very common to use trace()
-calls to output the value of certain variables for debugging. However, this approach doesn't scale very well - at 60 fps, tracing the values of multiple variables results in a flood of messages. Breakpoints-debugging is great to inspect a game's internal state, but doesn't help when interrupting the execution is not an option, for example when debugging input logic.
This is where the watch window comes into play. It displays the values of variables using reflection. For example, to keep track of the player's position:
+FlxG.watch.add(_player, "x");
+FlxG.watch.add(_player, "y");
+
+The display string does not have to be the same as the variable's name, "numEnemies" is much more descriptive than "length" in this example:
+FlxG.watch.add(_enemies, "length", "numEnemies");
+
+
+For static variables, you pass the class instead of an object:
+FlxG.watch.add(FlxG, "height");
+
+It's also possible to edit the displayed values by clicking on them, entering a new value in the text field and pressing enter to confirm. This even works with FlxPoint
objects.
To remove a watch entry again, simply call FlxG.watch.remove(object, variableName)
.
Quick watches are a lightweight alternative to a regular watch entry. They don't require a variable, they simply store a value for a String
name. The following example stores the result of FlxG.keys.anyPressed(["UP", "W"])
under the name "Up key pressed"
- this is updated every frame since it happens in update()
.
override public function update():Void
+{
+ super.update();
+ FlxG.watch.addQuick("Up key pressed", FlxG.keys.anyPressed(["UP", "W"]));
+}
+
+To remove a quick watch entry, call FlxG.watch.removeQuick(name)
.
+Quick watch values can not be modified.
FlxG.watch.addMouse()
is a convenient helper to display the current mouse position in the watch window. This can be useful to find the right coordinates to position UI elements at. You can also use the console command watchMouse
to call this function.
The stats window displays some basic profiling info:
+update()
calls this frame (and the time it took in ms)draw()
calls this frame (and the time it took in ms)FlxQuadtree
pool for collision detectionFlxList
(used for quad trees) pool3 and 4 are especially useful when it comes to performance optimization ("Do I need to optimize my rendering or my update-logic?"). Of course this is only very basic data, profiling tools like Adobe Scout or hxScout provide much more detailed information.
+ +The Bitmap Log can be used to display BitmapData
objects via FlxG.bitmapLog.add(bitmapData)
. This can be useful to debug logic that manipulates some BitmapData
. The window provides a slideshow to scroll through logged bitmaps. You can use the middle mouse button to move the graphic around and the mouse wheel to zoom in and out.
You can also inspect flixel's internal BitmapData
cache by calling FlxG.bitmapLog.viewCache()
or entering the console command viewCache
.
The console allows a limited subset of Haxe code to be parsed and executed at runtime via hscript. Commands like state._player.x = 50
or state._player.jump()
as you'd expect. Especially on targets with long compile times, this can speed up development substantially.
It also supports auto completion for class fields and registered functions / objects / classes, similar to the completion popups in IDEs (albeit without documentation, which is not available at runtime).
+ +state
is the starting point for hscript and needs to be registered to the console to be available - Flixel already does this for you. The same goes for a few classes like FlxG
or Math
. To register further objects or classes, call FlxG.console.registerObject()
.
By default, the game is paused when the console text field receives focus. After a command is executed, the game progresses one frame so the effects can be seen.
+The console stores executed commands (use the up and down keys to cycle through them). This history is persistent across executions of your game (but not across different target platforms).
+Functions can also be registered to the console directly as commands via FlxG.console.registerFunction()
. Here's an example with a function called "spawnEnemy"
, spawning a new enemy at the current mouse position in the Mode demo.
// in PlayState#create()
+FlxG.console.registerFunction("spawnEnemy", function() {
+ var mousePos = FlxG.mouse.getWorldPosition();
+ var enemy = _enemies.recycle(Enemy);
+ enemy.init(Std.int(mousePos.x), Std.int(mousePos.y), _enemyBullets, _bigGibs, _player);
+});
+
+Tracker windows are a convenient way to inspect the most important properties of a class / object. Each tracker window is basically a watch window instance. It's the only window type that can be closed.
+A tracker profile defines the properties that should be watched for a specific class. Let's take a look at the pre-defined tracker profile for FlxSprite
:
new TrackerProfile(FlxSprite, ["frameWidth", "frameHeight", "alpha", "origin", "offset", "scale"], [FlxObject])
+
+The first argument determines the class the profile belongs to, the second is an Array<String>
containing the property names. The third argument is a list of extensions - in this case just FlxObject
. This means that the properties defined in the tracker profile of FlxObject
will be added to tracker windows for FlxSprite
as well. This works recursively - FlxObject
"extends" the FlxBasic
tracker profile, so any properties of that profile will be added as well. This is why FlxG.debugger.track(_player)
in Mode's PlayState#create()
creates a window with a longer list of properties than you'd initially expect from the FlxSprite
profile:
Alternatively, you can use the console to create tracker windows at runtime:
+track(FlxG.state._player)
The real power of tracker windows comes with the ability to define custom profiles, for example for the Player
class in Mode:
FlxG.debugger.addTrackerProfile(new TrackerProfile(Player, ["isReadyToJump", "_shootCounter", "_jumpPower"], [FlxBasic]));
+
+(Note: calling addTrackerProfile()
causes a crash on the latest haxelib release due to a bug. As a workaround, you can call FlxG.debugger.track(null);
beforehand).
Flixel's VCR feature (recording and replaying via FlxG.vcr
) is mostly disabled by default and can be activated with the FLX_RECORD
define. Even so, a few features are still available by default via the middle three buttons of the debugger:
The left button resets the current state via FlxG.resetState()
.
The middle button pauses / unpauses the game.
+The right button pauses the game if it isn't already paused and skips ahead exactly one frame (one update()
and one draw()
call). This can be very useful for debugging certain issues.
If FLX_RECORD
is defined, two more buttons are available:
The circle starts a new recording - it also resets the current state, since the VCR does not support recordings that start mid-state. If a recording has already been started, the button stops it and opens a file dialog to save it.
+The button with the folder icon right allows loading record files and replaying them.
+You can add custom buttons to the debugger header using FlxG.debugger.addButton()
. This is what FlxNapeState
from flixel-addons does if you use it - it adds a convenient "N" button to toggle Nape's debug draw.
Debugger buttons are persistent across states - FlxG.debugger.removeButton()
has to be called manually to remove state-specific buttons.
A debugger layout determines the initial position and size of each debugger window. The available layouts are described in the FlxDebuggerLayout enum. You can change the current layout by calling FlxG.debugger.setLayout()
.
Here's an example of FlxDebuggerLayout.RIGHT
:
The Interaction Tool, enabled by the icon in the debugger overlay, allows you to change game elements, e.g. move sprites, while the game is still running. Click here to learn more about the interaction tool.
+ + + +The desktop targets of HaxeFlixel run on all mainstream operating systems (OSX, Windows and Linux). They are all compiled to C++ through the Hxcpp library and are rendered through interfacing with the Simple DirectMedia Layer. The code for this part of the library is developed through NME which provides dll libs that openfl interfaces with.
+The biggest advantage of desktop targets compared to web and mobile is the power of desktop CPU and GPU processors. More complex scenes, physics and number of objects rendered can give a larger creative freedom in the games you create.
+Rendering in HaxeFlixel is done through the drawTiles API. OpenGL textures are used with the GPU to render Flixel sprites. This native C++ code and use of the GPU outperforms runtimes such as the Flash Player and Adobe AIR in most circumstances substantially.
+#if cpp
+//your desktop code
+#end
+
+#if desktop
+//your desktop code
+#end
+
Desktop platforms can use a window width and height of 0, which is a special value that uses the full resolution of the current display.
+<window width="0" height="0" background="#FFFFFF" fps="60" />
+
+OpenFL also exposes the following specific settings for the desktop target:
+<window hardware="true" allow-shaders="true" require-shaders="true" if="cpp"/>
+<window vsync="true" antialiasing="4" if="cpp" />
+<window orientation="portrait" /> || <window orientation="landscape" if="cpp"/>
+
+Visual Studio Code, FlashDevelop and IntelliJ IDEA support CPP desktop compilation through their GUI.
+The basic command to compile and test a native desktop target:
+lime test windows
+lime test mac
+lime test linux -64
+
Run this command from the root folder of your project, the default project.xml will be used automatically. Using the test command will automatically launch the application created.
+ + +What would a dungeon game be without enemies? Let's add some!
+This should be second nature by now - add two new entity types in your Ogmo project, enemy
and boss
:
+
Then scatter some enemies and a boss around the map.
++
So we want to have 2 different enemies in our game. We'll need spritesheets for both of them, with 16x16 pixel frames and the same animation frames as our player. Name them enemy.png
and boss.png
and put them in the assets/images
folder. You can use these, if you want (thanks, again, Vicky!):
+
+
Note: make sure that your enemy sprites are functionally the same - they should have the same number of frames for each facing
animation.
Let's add a new some code for enemies. Since we're going to have two different types of enemies, regular enemies and the boss, let's start by creating an EnemyType
enumeration:
enum EnemyType
+ {
+ REGULAR;
+ BOSS;
+ }
+
+ This basically just gives us two handy constants that we can use to distuingish them. We will put both the enum
and our Enemy
class into the same Enemy.hx
"module" (that's what .hx
files are called). The class is going to look very similar to our Player
:
package;
+
+ import flixel.FlxSprite;
+
+ enum EnemyType
+ {
+ REGULAR;
+ BOSS;
+ }
+
+ class Enemy extends FlxSprite
+ {
+ static inline var WALK_SPEED:Float = 40;
+ static inline var CHASE_SPEED:Float = 70;
+
+ var type:EnemyType;
+
+ public function new(x:Float, y:Float, type:EnemyType)
+ {
+ super(x, y);
+ this.type = type;
+ var graphic = if (type == BOSS) AssetPaths.boss__png else AssetPaths.enemy__png;
+ loadGraphic(graphic, true, 16, 16);
+ setFacingFlip(LEFT, false, false);
+ setFacingFlip(RIGHT, true, false);
+ animation.add("d_idle", [0]);
+ animation.add("lr_idle", [3]);
+ animation.add("u_idle", [6]);
+ animation.add("d_walk", [0, 1, 0, 2], 6);
+ animation.add("lr_walk", [3, 4, 3, 5], 6);
+ animation.add("u_walk", [6, 7, 6, 8], 6);
+ drag.x = drag.y = 10;
+ setSize(8, 8);
+ offset.x = 4;
+ offset.y = 8;
+ }
+
+ override public function update(elapsed:Float)
+ {
+ if (velocity.x != 0 || velocity.y != 0)
+ {
+ if (Math.abs(velocity.x) > Math.abs(velocity.y))
+ {
+ if (velocity.x < 0)
+ facing = LEFT;
+ else
+ facing = RIGHT;
+ }
+ else
+ {
+ if (velocity.y < 0)
+ facing = UP;
+ else
+ facing = DOWN;
+ }
+ }
+
+ switch (facing)
+ {
+ case LEFT, RIGHT:
+ animation.play("lr_" + action);
+
+ case UP:
+ animation.play("u_" + action);
+
+ case DOWN:
+ animation.play("d_" + action);
+
+ case _:
+ }
+
+ super.update(elapsed);
+ }
+ }
+
+ The main difference is that we have a new type
variable, which we will use to figure out which enemy sprite to load, and which one we're dealing with, etc.
Next, we'll make a FlxGroup
in our PlayState
to hold our enemies, and load them into the map, very much the same way we did our coins.
At the top of our class, add:
+ var enemies:FlxTypedGroup<Enemy>;
+
+In the create function, right after we add our coin group:
+ enemies = new FlxTypedGroup<Enemy>();
+ add(enemies);
+
+ We will also need to add two more cases to our placeEntities()
function:
else if (entity.name == "enemy")
+ {
+ enemies.add(new Enemy(entity.x + 4, entity.y, REGULAR));
+ }
+ else if (entity.name == "boss")
+ {
+ enemies.add(new Enemy(entity.x + 4, entity.y, BOSS));
+ }
+
+Go ahead and test out your game to make sure the enemies are added properly.
+(optional step) Our placeEntities()
is starting to get a bit repetitive. Each if
checks entity.name
, and each time we use entity.x
and entity.y
.
Let's fix this by using a switch-case
instead of an if
/else
-chain, as well as adding some temporary x
and y
variables:
var x = entity.x;
+ var y = entity.y;
+
+ switch (entity.name)
+ {
+ case "player":
+ player.setPosition(x, y);
+
+ case "coin":
+ coins.add(new Coin(x + 4, y + 4));
+
+ case "enemy":
+ enemies.add(new Enemy(x + 4, y, REGULAR));
+
+ case "boss":
+ enemies.add(new Enemy(x + 4, y, BOSS));
+ }
+
+There, that's a lot easier to read!
+Now let's give our enemies some brains.
+In order to let our enemies 'think', we're going to utilize a very simple Finite-state Machine (FSM). Basically, the FSM works by saying that a given machine (or entity) can only be in one state at a time. For our enemies, we're going to give them 2 possible states: Idle
and Chase
. When they can't 'see' the player, they will be Idle
- wandering around aimlessly. Once the player is in view, however, they will switch to the Chase
state and run towards the player.
Shouldn't be that hard! First, we'll make our FSM
class:
class FSM
+ {
+ public var activeState:Float->Void;
+
+ public function new(initialState:Float->Void)
+ {
+ activeState = initialState;
+ }
+
+ public function update(elapsed:Float)
+ {
+ activeState(elapsed);
+ }
+ }
+
+Next, we'll change our Enemy
class a little.
We need to define these variables at the top of the class:
+ var brain:FSM;
+ var idleTimer:Float;
+ var moveDirection:Float;
+ var seesPlayer:Bool;
+ var playerPosition:FlxPoint;
+
+At the end of the constructor, add:
+ brain = new FSM(idle);
+ idleTimer = 0;
+ playerPosition = FlxPoint.get();
+
+And then add the following functions:
+ function idle(elapsed:Float)
+ {
+ if (seesPlayer)
+ {
+ brain.activeState = chase;
+ }
+ else if (idleTimer <= 0)
+ {
+ // 95% chance to move
+ if (FlxG.random.bool(95))
+ {
+ moveDirection = FlxG.random.int(0, 8) * 45;
+
+ velocity.setPolarDegrees(WALK_SPEED, moveDirection);
+ }
+ else
+ {
+ moveDirection = -1;
+ velocity.x = velocity.y = 0;
+ }
+ idleTimer = FlxG.random.int(1, 4);
+ }
+ else
+ idleTimer -= elapsed;
+
+ }
+
+ function chase(elapsed:Float)
+ {
+ if (!seesPlayer)
+ {
+ brain.activeState = idle;
+ }
+ else
+ {
+ FlxVelocity.moveTowardsPoint(this, playerPosition, CHASE_SPEED);
+ }
+ }
+
+ Also add this line to update()
before super.update(elapsed)
:
brain.update(elapsed);
+
+ The way this is going to work is that each enemy will start in the Idle
state. In the PlayState
we will have each enemy check to see if it can see the player or not. If it can, it will switch to the Chase
state, until it can't see the player anymore. While in the Idle
state, every so often (in random intervals) it will choose a random direction to move in for a little while (with a small chance to just stand still). While in the Chase
state, they will move directly towards the player.
Let's jump over to the PlayState
to add our player's vision logic. In update()
, under the overlap and collision checks, add:
FlxG.collide(enemies, walls);
+ enemies.forEachAlive(checkEnemyVision);
+
+Next, add the checkEnemyVision()
function:
function checkEnemyVision(enemy:Enemy)
+ {
+ if (walls.ray(enemy.getMidpoint(), player.getMidpoint()))
+ {
+ enemy.seesPlayer = true;
+ enemy.playerPosition = player.getMidpoint();
+ }
+ else
+ {
+ enemy.seesPlayer = false;
+ }
+ }
+
+ Note how we need to modify two enemy variables for this. The default visibility in Haxe is private
, so the compiler doesn't allow this. We will have to make them public
instead:
public var seesPlayer:Bool;
+ public var playerPosition:FlxPoint;
+
+That's all there is to it! Try out your game and make sure it works.
+ +Next, we'll add some UI to the game, and add our RPG-style combat so you can fight the enemies!
+ + +No, you have to learn HaxeFlixel to use HaxeFlixel, although previous experience with the AS3 version will help.
+No, HaxeFlixel abstracts it completely.
+We have a dedicated Community Tutorials page that links to many external community made tutorials!
+You can find the official HaxeFlixel "Dungeon Crawler" tutorial here.
+On the official GitHub repository.
+On some platforms, it helps to manually cache sounds: FlxG.sound.cache("sound");
or FlxG.sound.cacheAll();
to do all at once.
Collisions are limited to the area defined by FlxG.worldBounds
. For example in platformers where this area needs to be larger, you need to adjust it manually.
No, the framework is limited to 2D graphics. Well. Except for some crazy people.
+If you add embed="true"
to the <assets path="assets">
tag of the Project.xml
, the asset files are embedded into the .exe
.
The addons are a set of very useful, but nevertheless optional classes that not every game is going to need. That is why they are not quite important enough to be a part of HaxeFlixel's core.
+The addons are designed to be used with haxelib and are hosted on the main HaxeFlixel GitHub account.
+haxelib install flixel-addons
+
Now to verify you can see if flixel-addons is in your haxelib list:
+haxelib list
+
To use the addons in a project simply add the following XML node to your OpenFL Project.xml file.
+<haxelib name="flixel-addons" />
+
The Flixel Display list is a specially designed structure for your game's sprites to be rendered.
+FlxSprite
!= flash.display.Sprite
The Flash API has a display list that is populated by adding display objects in a parent child relationship. Sprite
s are an extended form of display objects that have extended features. They can be added, removed and can have their parent and children's depth changed with an easy to use API.
It is a common misconception for Flash developers to assume that FlxSprite
s in Flixel work like the Flash Sprite
s. FlxSprite
s also do not share the event system Flash Sprite
s have, so addEventListener()
is also not available or necessary in most situations.
For performance reasons, Flixel has its own independent display list and it renders all of its FlxSprite
s onto a single Flash DisplayObject
with each FlxCamera
. For this reason, you cannot add a flash.display.sprite
to a FlxState
and you cannot add a FlxSprite
to the main Flash stage.
You can see the display objects used in a typical Flixel game in this diagram. Note you can still place display objects above or below Flixel's camera. It is recommended to use FlxG.addChildBelowMouse()
and FlxG.removeChild()
for that.
To make HaxeFlixel development easier, a set of command line tools has been developed with Haxe and Neko. With it you can easily create our demo projects, templates and more. Just like HaxeFlixel it is an open-source tool and additions/improvements from the community are welcome at the flixel-tools GitHub repository.
+The tools are available on haxelib:
+haxelib install flixel-tools
+
+To set the tools up initially / to be able to use the flixel
alias in your console:
haxelib run flixel-tools setup
+
+create
(c
)Create a new demo (in the current directory):
+flixel create <name_or_number>
+
+If no name or number is given, it will list all demos and prompt you for a choice, by number or name.
+template
(tpl
)To create a new project from the default template:
+flixel tpl -n <name>
+
+Any folder in the flixel-templates
haxelib is treated as a template and can be created with the flixel tpl <foldername>
syntax. This makes it easy to create custom templates.
import flixel.group.FlxGroup;
+
+FlxGroups are an invaluable method of grouping your game objects in Flixel. In fact, there is no way around them, as FlxState
itself extends FlxGroup
.
+The use cases for groups are vast: pooling to reuse objects, collision detection and setting up easy ways to access particular collections of objects. A great feature of FlxGroups are their ability to be nested when using collision detection. Doing collisions is also way more efficient than doing it for individual objects.
The API for FlxGroups is similar to other places in Flixel. Here is a basic example using a group to pool bullet objects, represented by a Bullet
class, holding a maximum of 100 bullets:
var poolSize = 100;
+var bullets = new FlxTypedGroup<Bullet>(poolSize);
+
+for (i in 0...poolSize)
+{
+ var bullet = new Bullet();
+ bullet.kill();
+ bullets.add(bullet);
+}
+
+Note how we did not use a regular FlxGroup
, but a FlxTypedGroup<Bullet>
. This means that this particular group can only store objects that are instances of the Bullet
class or instances of Bullet
subclasses. FlxGroup
itself is nothing but a shortcut for FlxTypedGroup<FlxBasic>
.
Now, say we want to retrieve bullet instance to use in a shoot method:
+var bullet = bullets.recycle(Bullet);
+
+If we had used a regular group here, we would have had to cast
the return value of recycle()
to a Bullet
. By using a FlxTypedGroup<Bullet>
, we have the benefit of type-safety.
You can add any type of Object that extends the base FlxGroup type, if you use FlxGroup
this is FlxBasic
.
Removes an object from a group, it will return the object you removed.
+This will let you get the first FlxBasic object that has exists == false, this is typically used in object pooling when object may have used the kill() method. Note if you destroy() objects they will be made null in the FlxGroup and you wont be able to reuse them.
+This will recursively kill() all objects so that they will be ready for use with getFirstAvailable.
+Call this function to sort the group according to a particular value and order. You will need to specify a sorting function to do so. If you want to sort by something other than y, you will have to write a custom sorting function.
+Otherwise, you can just use the pre-made FlxSort.byY()
like so for Zelda-style-sorting:
group.sort(FlxSort.byY);
+
+
+
+ import flixel.util.FlxSave;
+
+HaxeFlixel gives you the FlxSave
class to manage saving and loading your game's data. You might use it to save and load a high score table, or the position and status of the player and enemies, or custom settings the player has selected. Like most of HaxeFlixel, FlxSave
is cross-platform in functionality.
To use FlxSave
you need a variable typed to that class. You can create your own variable (perhaps in your registry class, in your gameplay FlxState
, or as a local variable in your save/load functions). You may also use the one that HaxeFlixel itself uses (FlxG.save
).
The example code used below is largely taken from the HaxeFlixel save demo. If you have already installed HaxeFlixel, then to install the demo project just open a command line utility, navigate to the folder you would like to install into, and enter the command flixel create Save
, or download the source via GitHub using the link on the demo web page.
So how can you save some of your game data? Once you have your variable, you will need to initialize, then bind it:
+_gameSave = new FlxSave(); // initialize
+_gameSave.bind("SaveDemo"); // bind to the named save slot
+
+Note the string "SaveDemo". This is how HaxeFlixel tracks what save slot you are binding to in local storage. If you want to have multiple saves, you will probably want to define a series of strings to identify each slot, eg. "SaveSlot1", "SaveSlot2", etc. and bind to the appropriate one. For more information on using multiple save slots, take a look at Wolfgang's article on the subject for AS3 Flixel, but keep in mind that the AS3 syntax is a little different from Haxe.
+Note: If you plan to use FlxG.save
you can skip the initializing and binding steps, as HaxeFlixel has done it for you.
Once bound, the save is essentially "live". To write to it you use the .data property of variable, treating it as an object:
+_gameSave.data.boxPositions = new Array<FlxPoint>();
+_gameSave.data.boxPositions.push(box.getPosition());
+
+_gameSave.data.enemy = myEnemy;
+
+// save data
+_gameSave.flush();
+
+Writes the local shared object to disk immediately. +Required on non-Flash targets.
+In certain cases you may need to serialize and unserialize your data (fancy words for "take my data and turn it into a specially formatted string, or back into data") to avoid errors, but you may want to try it without serialization unless you experience problems.
+In order to retrieve your saved data, you simply make sure you have a correctly bound FlxSave
variable and read each value from the .data
property.
var position = _gameSave.data.boxPositions[tempCount];
+box.setPosition(position.x, position.y);
+
+This means that, depending on your save needs, when loading a save slot you may need to loop through a long list of data to assign each of the values back to it's correct home.
+When you save data to a given FlxSave
save slot there is, of course, the possibility that data already exists in that slot (hopefully your saved data from an earlier save). One way to test for this is to check if one of your variables is null. If save data does already exist and you plan to save an entire fresh set of new data, then to avoid carrying over values from an earlier save you may wish to either go through and initialize or reset each of the potentially saved variables to some default value (or null) before saving your new set, or you may wish to erase the save data entirely (probably not a good idea if you're using FlxG.save
).
FlxSave
provides an .erase()
method to help with this process, but keep in mind that calling it on a bound FlxSave
variable will: immediately erase all the data in .data, save the slot in the erased state (any earlier data is now completely gone), and also destroy the binding to the save slot. This last point is important to note, as after the binding has been broken FlxSave
may still let you assign to .data and even call other methods without errors to indicate that the data is not actually being stored at all. So if you do use the .erase()
method, don't forget to call .bind()
again before you save or load any further data.
In the Save demo the application creates and binds a FlxSave
variable when the demo state initializes (see PlayState#create()
), and then leaves this variable accessible for loading and saving from that point on. This allows you to continually update the save object when necessary, but it's equally valid to create, initialize, and bind a FlxSave variable only when loading and saving. In that case you should familiarize yourself with .close()
and .destroy()
for safe and efficient handling of your FlxSave
.
To review these additional methods and check out any other FlxSave
functionality in more detail, take a look at the API documentation or look at the class definition itself (currently under the util package in the flixel library).
import flixel.FlxSprite;
+
+FlxSprites are the core building blocks of all Flixel games. They offer a friendly API to add animation, movement and features for the needs of most games.
+It is pretty common place to extend FlxSprite
for your own game's needs; for example a SpaceShip
class may extend FlxSprite
but could have additional variables for the game like shieldStrength
or shieldPower
. When you extend FlxSprite
it is important to remember to use super.update()
if you override the update
method, as you would do for any other FlxBasic
.
This method is the easiest way to use a single image for your FlxSprite. Using the OpenFL asset system defined in the project xml file you simply have to define a path to your image and the compiler will do the rest.
+var player = new FlxSprite();
+player.loadGraphic("assets/player.png");
+add(player);
+
+This method is a handy way to make a simple color fill to quickly test a feature or have the basic shape.
+var whiteSquare = new FlxSprite();
+whiteSquare.makeGraphic(200, 200, FlxColor.WHITE);
+add(whiteSquare);
+
+whiteSquare.x = 100;
+whiteSquare.y = 300;
+
+Automatically set in loadGraphic()
or makeGraphic()
, changing this will only affect the hitbox of this sprite, use scale
to change the graphic's size.
// get
+var getWidth = whiteSquare.width;
+
+// set
+whiteSquare.width = 100;
+whiteSquare.height = 100;
+
+(FlxPoint)
+Change the size of your sprite's graphic. NOTE: The hitbox is not automatically adjusted, use updateHitbox()
for that (or setGraphicSize()
).
// twice as big
+whiteSquare.scale.set(2, 2);
+
+// 50%
+whiteSquare.scale.set(0.5, 0.5);
+
+(FlxPoint) +Controls the position of the sprite's hitbox. Likely needs to be adjusted after changing a sprite's width, height or scale.
+whiteSquare.offset.set(50, 50);
+
+(FlxPoint) +Rotation axis. Default: center.
+WARNING: If you change this, the visuals and the collisions will likely be pretty out-of-sync if you do any rotation.
+// rotate from top-left corner instead of center
+whiteSquare.origin.set(0, 0);
+
+This method is useful for when you want to hide a sprite from the stage but keep it available to reuse later. For example you may want to respawn an enemy the player has killed.
+This method is destructive to the sprite and should be used when you want to make sure that the sprite will be cleared from memory. It is commonly used inside a FlxState's overridden destroy method.
+Flixel supports spritesheet animation.
+ +player.loadGraphic("assets/player.png", true, 32, 36);
+player.animation.add("walk", [0, 1, 0, 2], 5, true);
+player.animation.play("walk");
+
+
+
+ This is the basis for your game's levels and menus, each described in a "state" structure. The state is a way of organising your game objects for the state that the game is currently in. For example, when you create level 0 of your game it's much more organized to only have the code for that level, not every level. Also it's good to use switching states to clear your memory, to avoid memory leaks. A typical Flixel game will have a separate FlxState
class for every level and menu.
In each FlxState
all the FlxSprites are added to be rendered.
This is where you setup and create all your state's objects; for example your level tilemaps, your player sprites, spawn your initial enemies. Flixel runs this method before it starts to render your state so its the perfect place.
+This is the place where you add your sprites, tilemaps etc to your state to be rendered. It works similar to OpenFL's display list API with addChild()
.
This is the place where you remove sprites etc you have added to your state. Everything you remove still exists so you can add it back later. If you're not going to use the removed object again you might want to consider removing it from memory by setting it as null for example.
+This is the place where you can run code on every frame of your game. It's where you setup your input controls, trigger movement and almost all of your gameplay logic.
+package;
+
+import flixel.FlxState;
+
+class FlxExampleState extends FlxState
+{
+ override public function create():Void
+ {
+ //create your state objects here
+ }
+
+ override public function update(elapsed:Float):Void
+ {
+ //call super to update the core state class
+ super.update(elapsed);
+ }
+}
+
+Here is an example of a simple game state;
+package;
+
+import flixel.tile.FlxTilemap;
+import flixel.FlxG;
+import flixel.FlxSprite;
+import flixel.FlxState;
+import flixel.graphics.FlxGraphic;
+
+class FlxExampleState extends FlxState
+{
+ var wizard:FlxSprite;
+ var level:FlxTilemap;
+
+ override public function create():Void
+ {
+ //create a main player
+ wizard = new FlxSprite(200, 200, 'assets/player.png');
+ wizard.maxVelocity.set(80, 200);
+ wizard.acceleration.y = 200; // gravity
+ wizard.drag.x = wizard.maxVelocity.x * 4;
+ add(wizard);
+
+ //create a tilemap level
+ level = new FlxTilemap();
+ level.loadMap('assets/level.csv', FlxGraphic.fromClass(GraphicAuto), 0, 0, AUTO);
+ add(level);
+ }
+
+ override public function update(elapsed:Float):Void
+ {
+ //control the player with keyboard
+ wizard.acceleration.x = 0;
+
+ if (FlxG.keys.pressed.LEFT)
+ {
+ wizard.acceleration.x = -wizard.maxVelocity.x * 4;
+ }
+ if (FlxG.keys.pressed.RIGHT)
+ {
+ wizard.acceleration.x = wizard.maxVelocity.x * 4;
+ }
+ if (FlxG.keys.justPressed.SPACE && wizard.isTouching(FLOOR))
+ {
+ wizard.velocity.y = -wizard.maxVelocity.y / 2;
+ }
+ super.update(elapsed);
+ }
+}
+
+
+
+ A FlxTween
allows you to create smooth interpolations and animations easily. Tweening is short for inbetweening: you only have to specify start and end values and the FlxTween
class will generate all values between those two. If you want to see a FlxTween
in action, this tween demo is available.
For example, if you want to move a FlxSprite
across the screen, this code snippet would do it:
sprite.x = 200;
+sprite.y = 200;
+
+FlxTween.tween(sprite, { x: 600, y: 800 }, 2);
+
+The first two lines specify the start position of the sprite, because the tween()
method assumes the current position is the starting position.
The first parameter is the object you want to act upon; the second parameter is the map which contains the properties you want to interpolate, and their desired target values. Here, we want to translate the sprite in x to position 600 and in y to position 800. The third parameter specifies the duration of the tween in seconds, which in this case is 2 seconds.
+If you start a tween using the code above, it will run until the desired values are reached, then stop. As the tween()
method returns an object of type FlxTween
, keeping this object in a variable allows you to access the current tween running if you wish to control it.
For example, this code stops the translation of the sprite if the player presses the spacebar of their keyboard:
+var tween:FlxTween;
+
+public function new()
+{
+ super();
+ // set up sprite
+ tween = FlxTween.tween(sprite, { x:600, y:800 }, 2);
+}
+
+override public function update(elapsed:Float)
+{
+ super.update(elapsed);
+
+ if (FlxG.keys.justPressed.SPACE)
+ tween.cancel();
+}
+
+The tween()
method takes an optional fourth parameter which is a map of options.
Possible values are:
+type
: choose one of these:
FlxTween.PERSIST: stops when it finishes. Unlike ONESHOT, this type of tween stays attached to the core container when it finishes. This means you can keep a reference to this tween and call start()
whenever you need it. This does not work with ONESHOT;
FlxTween.LOOPING: restarts immediately when it finishes;
+FlxTween.PINGPONG: plays tween "hither and thither". This is like LOOPING, but every second execution is in reverse direction;
+FlxTween.BACKWARD: plays tween in reverse direction.
+onComplete
: a callback function, which is called once the tween has finished. This is called every time the tween has finished one execution and comes in handy for repeating tweens (LOOPING and PINGPONG). The method must take a FlxTween
and return nothing.
ease
: an optional easer function. This can be used to make the beginning and end of a tween smoother. The FlxEase
class provides many static methods for this which should cover most cases. The following list shows all functions from FlxEase
. In all of these, In
can be replaced by Out
or InOut
, depending on where you want to apply the easing effect: at the beginning of the animation, at the end or at both sides.
backIn
bounceIn
circIn
cubeIn
elasticIn
expoIn
quadIn
quartIn
quintIn
sineIn
startDelay
: time to wait before starting this tween, in seconds.
loopDelay
: time to wait before this tween is repeated, in seconds. This only applies to LOOPING and PINGPONG.
For example:
+public function new()
+{
+ super();
+ // set up sprite
+ sprite.x = 200;
+ sprite.y = 200;
+ FlxTween.tween(sprite, { x: 600, y: 800 }, 2, { type: FlxTween.PINGPONG, ease: FlxEase.quadInOut, onComplete: changeColor, startDelay: 1, loopDelay: 2 });
+}
+
+function changeColor(tween:FlxTween):Void
+{
+ // change the color of the sprite here
+}
+
+This code moves the sprite constantly between the two points (200|200) and (600|800), smoothly accelerating and decelerating. Each time the sprite arrives at one of those two points, its color changes. The animation starts after 1 second and then the sprite pauses at each point for 2 seconds.
+There are many more tweening methods in FlxTween
, which are used for special cases:
color()
Tweens the red, green and blue part of a color independently, because normal tweening would screw up the colors.
+Usage: color(Sprite : FlxSprite
, Duration : Float
, FromColor : Int
, ToColor : Int
, ?FromAlpha : Float
, ?ToAlpha : Float
, ?Options : TweenOptions
)
Notice that unlike in the tween()
method, the duration is specified before the color values and you have to enter the start and the end value. The options are the same as described above.
angle()
This method is for tweening the angle of a FlxSprite
.
Usage: angle(Sprite : FlxSprite
, FromAngle : Float
, ToAngle : Float
, Duration : Float
, ?Options : TweenOptions
)
The FlxTween
class also contains the methods linearMotion()
, quadMotion()
, cubicMotion()
and circularMotion()
, which make objects follow straight lines, smooth paths or circles.
The methods linearPath()
and quadPath()
can be used for longer paths defined through an array of points, instead of a fixed number of points.
If you want to use these methods please refer to the FlxTween
API.
A concise list of tools useful to HaxeFlixel developers.This list is under construction. Suggest more, here.
+A list of repos and libraries made with the purpose of extending HaxeFlixel's capabilities. One day, these may be officially incorporated into HaxeFlixel, but for now they serve a niche demand on their own.
+A library of extensions, utilities, and other helpful classes for making games quickly in haxeflixel! There's also a framework agnostic version simply called Zerolib
+djFlixel is a small library with some helpful tools for HaxeFlixel, including a multi-page menu system, keyboard/gamepad helpers, and very fun effects.
+A repository made by CheemsAndFriends and DotWith made for playing all spritesheet formats and the mysterious but interesting export called Texture Atlas
Yagp's Gif Player for HaxeFlixel.
+A library which adds native video support for OpenFL and HaxeFlixel.
+Flixel Depth is a fun 3D hack for making orthogonally 3D looking games using haxeflixel. It does so primarily by offsetting sprites according to the camera's current angle and scaling the primary game window down. This is NOT real 3D, you will still be making a 2D game, so expect a lot of edge cases!
+A library aimed at making Stacked Sprites easy with Haxeflixel!
+An advanced bitmap based image editor that has many capabilities similar to commercial software. It can be used as a simple paint program, an expert quality photo retouching program, an online batch processing system, a mass production image renderer, an image format converter, etc.
+With an easy-to-learn but powerful set of tools, Paint.NET can be used for photo retouching, tilemap editing, pixel art editing, and so on. It contains advanced features such as levels and curves editing, noise addition and reduction, fractal and Perlin noise generation and more, but can also be used as a simple image editor.
+An advanced vector image editor with an advanced editing toolset. Inkscape has capabilities similar to commercial software such as Adobe Illustrator, CorelDraw, or Xara X. Inkscape leverages the W3C standard Scalable Vector Graphics (SVG) file format. +Inkscape supports many advanced SVG features (markers, clones, alpha blending, etc.) and great care is taken in designing a streamlined interface. It is very easy to edit nodes, perform complex path operations, trace bitmaps and much more.
+Krita is a free digital painting and illustration application. Krita offers CMYK support, HDR painting, perspective grids, dockers, filters, painting assistants, and many other features you would expect.
+Aseprite is a program designed for drawing pixel art. It has layers, animation tools, tile map tools, blend modes, and many more useful features. It's not free, but it's a favorite for many developers.
+Audacity is free, open source, cross-platform audio software for multi-track recording and editing.
+LMMS is a free cross-platform software which allows you to produce music with your computer.
+An OpenFL based sound effects generator for video games, build by Tom Vian of SFBGames. Available on HTML5, Windows, and Mac!
+LabChirp is a program for creating sound effects.
+Tiled is a general purpose tile map editor. It is meant to be used for editing maps of any tile-based game, be it an RPG, a platformer or a Breakout clone. This demo shows how to load a Tiled map.
+Source code: https://github.com/mapeditor/tiled
+Ogmo Editor is a generic level editor for indie game developers who use Windows. It also happens to be free and open source. The editor is built to be reconfigurable, so you can set it up to work well for your game project. This HaxeFlixel tutorial demonstrates how to create a simple top-down level with Ogmo.
+Source code: https://github.com/Ogmo-Editor-3/OgmoEditor3-CE
+LDtk is a level editor created by the lead game designer of Dead Cells. This editor has some extra features work nicely with Haxe and HaxeFlixel, and focuses on being easy to use. This tutorial shows you how to use LDtk tile maps with HaxeFlixel.
+Source code: https://github.com/deepnight/ldtk
+ShoeBox is a free Adobe Air based app for Windows and Mac OS X with game and UI related tools. It has many utilities that make asset management easier, such as sprite packing, sprite extraction, texture ripping, bitmap font creation, and more.
+Texture Packer does one thing very well - it packs several sprites into one large image to help reduce load times for your game. It's easy to use and easy to integrate with HaxeFlixel. This demo shows how to use a TexturePackerAtlas.
+🎮 📝 A Github list of Game Development resources to make magic happen.
+Curated list of tools and software of all kind, useful for non-programming related things such as art, animation and music!
+(Likely outdated) Ludum Dare hosts a great list of tools for general game development.
+(Likely outdated) Huge list of resources for game dev. A must read for indie developers.
+ + +Our game is really starting to come together! Now we need it to feel more like a 'game' with a win and lose scenario. For our (very simple) game, we'll just make it so that if you ever die in combat, you get a Game Over, and when you beat the boss enemy, you win. Both of these conditions will take you to the same FlxState
to show you your score and allow you to play again if you want.
Let's start with PlayState
. We need to add some flags to see if we're ending the game, and if the player has 'won' or not. So, add:
var ending:Bool;
+ var won:Bool;
+
+To the top of the class.
+Next, in update()
, right under super.update(elapsed)
add:
if (ending)
+ {
+ return;
+ }
+
+We don't want to allow anything else to go on if we're ending the game and getting ready to switch states.
+Next, still in update()
, we're going to change our logic in if (inCombat)
to this:
if (!combatHud.visible)
+ {
+ health = combatHud.playerHealth;
+ hud.updateHUD(health, money);
+ if (combatHud.outcome == DEFEAT)
+ {
+ ending = true;
+ FlxG.camera.fade(FlxColor.BLACK, 0.33, false, doneFadeOut);
+ }
+ else
+ {
+ if (combatHud.outcome == VICTORY)
+ {
+ combatHud.enemy.kill();
+ if (combatHud.enemy.type == BOSS)
+ {
+ won = true;
+ ending = true;
+ FlxG.camera.fade(FlxColor.BLACK, 0.33, false, doneFadeOut);
+ }
+ }
+ else
+ {
+ combatHud.enemy.flicker();
+ }
+ inCombat = false;
+ player.active = true;
+ enemies.active = true;
+ }
+ }
+
+ This will now check to see if the outcome was DEFEAT
, and if it was, it will set our ending flag to true, and then tell the camera to start fading out - calling doneFadeOut()
when it's done.
Similarly, if the outcome was VICTORY
, and the enemy that was just defeated was type 1 (the boss), we set our won
flag to true
, and also start fading out.
When the camera is done fading to black, we call this function, which will switch the state to our GameOverState
(which you'll make in a second), passing it if the player won or not, and how much money they have.
function doneFadeOut()
+ {
+ FlxG.switchState(new GameOverState(won, money));
+ }
+
+Finally, we need to add the GameOverState
. This is going to be a pretty simple FlxState
where we show a message - either "Game Over" or "You Win!", depending on our won flag, and the final score for this player. We will also use flixel's save/load functionality to compare the previous highscores, and, if the new score is higher, replace the saved highscore, and show the highscore on the screen.
Finally, we have a button to take the player back to the main menu.
+Here is the code for that State:
+ +If you test your game, you should be able to trigger the GameOverState
by either dying in combat or defeating the boss, and then clicking on the button in the GameOverState
will take you back to our MenuState
so you can play again. If all of that works, you're on the right track! But… our MenuState
is looking a little bland, now… let's fix that up!
Let's add a title and an options-button to the MenuState
:
var titleText:FlxText;
+ var optionsButton:FlxButton;
+
+Then, in create()
, we'll add them to the state (and move the play-button as well):
titleText = new FlxText(20, 0, 0, "HaxeFlixel\nTutorial\nGame", 22);
+ titleText.alignment = CENTER;
+ titleText.screenCenter(X);
+ add(titleText);
+
+ playButton = new FlxButton(0, 0, "Play", clickPlay);
+ playButton.x = (FlxG.width / 2) - playButton.width - 10;
+ playButton.y = FlxG.height - playButton.height - 10;
+ add(playButton);
+
+ optionsButton = new FlxButton(0, 0, "Options", clickOptions);
+ optionsButton.x = (FlxG.width / 2) + 10;
+ optionsButton.y = FlxG.height - optionsButton.height - 10;
+ add(optionsButton);
+
+Add the function that gets called when the options-button is clicked:
+ function clickOptions()
+ {
+ FlxG.switchState(new OptionsState());
+ }
+
+The OptionsState
that is called from the options button is fairly simple. It will contain a button to allow the user to clear the saved data (highscores, etc), as well as a simple FlxBar
to show the user the current volume level of the game with buttons to adjust it up or down. It will save the volume values so that each time the game starts, it will 'remember' what volume it was last set to (I know there's no sound…. yet ;))
The code for this State looks like this:
+ + The OptionsState
class uses a different spritesheet for the volume adjustment buttons (because they should appear smaller than the default buttons). This asset must have 3 frames, one to represent each button state: NORMAL
, HIGHLIGHT
, and PRESSED
. As long as we set up the frames in that order, the FlxButton
class will handle the rest.
Once again, you can make your own, or use the image below. Place it in the assets/images
folder, and you should be all set.
+
Finally, we want our game to load the stored volume (if there is any) each time the game starts, so, go to Main.hx
, and add this after the addChild()
call:
var save = new FlxSave();
+ save.bind("TurnBasedRPG");
+ if (save.data.volume != null)
+ {
+ FlxG.sound.volume = save.data.volume;
+ }
+ save.close();
+
+ Pretty simple: it makes a new FlxSave
object, binds it to our "TurnBasedRPG"
and then checks if there is a volume value stored in it, and if there is, sets our game's volume to match, and then closes the save.
Test everything out, make sure it's working, and that if you change your volume under options and then exit the game, it retains the value the next time to get into the options screen.
+ +Looking good! Next time we'll give our volume something to do by adding sound and music!
+ + +Gamepad input for HaxeFlixel is provided through the FlxGamepad
class and is available through FlxG.gamepads
and the InputFrontEnd
.
Since gamepads have a variety of manufacturers their keycodes provided to HaxeFlixel API differ from model to model. HaxeFlixel provides mappings that map buttons and sticks to common IDs for convenient use. Mappings are available for:
+For most gamepads HaxeFlixel will automatically detect the model and abstract the API inputs under a common "universal" gamepad model based on the Xbox 360 layout. The underlying device-specific "raw" inputs are still available for you to poll directly, if you choose.
+Here's some example logic for basic detection using the "universal" gamepad API:
+import flixel.FlxG;
+import flixel.FlxState;
+import flixel.input.gamepad.FlxGamepad;
+
+class PlayState extends FlxState
+{
+ override public function update(elapsed:Float):Void
+ {
+ super.update(elapsed);
+
+ // Important: can be null if there's no active gamepad yet!
+ var gamepad:FlxGamepad = FlxG.gamepads.lastActive;
+ if (gamepad != null)
+ {
+ updateGamepadInput(gamepad);
+ }
+ }
+
+ function updateGamepadInput(gamepad:FlxGamepad):Void
+ {
+ if (gamepad.pressed.A)
+ {
+ trace("The bottom face button of the controller is pressed.");
+ }
+
+ if (gamepad.analog.justMoved.LEFT_STICK_X)
+ {
+ trace("The x axis of the left analog stick of the controller has been moved.");
+ }
+ }
+}
+
+In this case, gamepad.pressed.A
checks whether the bottom face button is pressed. On a PS4 controller this would be the "X" button, on an XBox 360 or XBox One controller this would be the "A" button.
Also, the gamepad.pressed.A
syntax is shorthand for gamepad.pressed.check(FlxGamepadInputID.A)
. You want to use the latter syntax if you need to check a variable (which would be the case if the user can customize their inputs).
If you wanted to check a device-specific input, you would use the checkRaw
function, like this: gamepad.pressed.checkRaw(PS4ID.X)
Device-specific inputs can be found in the flixel.input.gamepad.id
package.
If you want to support a controller that HaxeFlixel doesn't provide the IDs for, the following methods of FlxGamePad
methods should be helpful for working out what those IDs are:
Return the FlxGamepadInputID
value under the "universal" gamepad model:
firstPressedButtonID()
firstJustPressedButtonID()
firstJustReleasedButtonID()
Return the device-specific input ID value:
+firstPressedButtonRawID()
firstJustPressedButtonRawID()
firstJustReleasedButtonRawID()
FLX_NO_GAMEPAD
+
+HaxeFlixel includes a conditional to omit using gamepads for optimization purposes if you are developing for a platform such as mobile, or your game just isn't designed for them.
+ + +Welcome to HaxeFlixel! To setup your Windows, Linux or Mac system to start making games, there are three steps:
+ +After that, you have to choose an editor / IDE to work in. The most popular option is Visual Studio Code (VS Code) which has it's own pages here:
+ +A more comprehensive list of options can be found on haxe.org.
+Time to complete this installation is close to 20 minutes (depending on the speed of your Internet connection for downloading the components).
+If you need more help, reach out via any of the communication channels.
+Note: this guide is a community effort. If you think you can help us improve it, please submit a pull request on +GitHub - each page has an "Edit" button in the upper right corner for this.
+ + +Now it's time to think about what we actually want our game to be, and how we're going to pull it off.
+The game we're going to help you build will be a very simple top-down 'dungeon crawler' game, where the player controls a single character, moves around a map, fights enemies and collects gold.
+So, what does that all actually translate to in HaxeFlixel?
+FlxState
where all the action happens, and figure out what we're going to put in the world.FlxTilemap
- and we'll draw our maps in another tool.FlxSprite
to represent the player on the screen. This will be the sprite that player will be able to control.FlxSprite
objects as well, and we'll have a 'fancy' way to have different types of enemies.FlxGroup
, FlxSprite
, FlxText
, FlxButton
objects and some other things.We'll tackle each of these things one-by-one.
+First up, let's create a simple menu. Eventually, we'll want a fancy MenuState
with a button for options, but for right now, we'll just have a button that says "Play" and switches to our PlayState
.
Go ahead and delete the line we added for our "Hello World" test within the PlayState
.
Now, right-click the source
folder in VSCode's Explorer, select New File
and enter MenuState.hx
.
Then, you can just copy the content of PlayState.hx
over, but replace the class name with MenuState
:
class MenuState extends FlxState
+ {
+
+ Right after the class declaration, we're going define a new variable. This is where you would define all the variables that you intend to use throughout a given class. We need to define a new FlxButton
variable to use as our 'play' button. So, type:
var playButton:FlxButton;
+
+ As before with FlxText
, the import should be added automatically. If not, you can also add missing imports via the light bulb that should appear after saving the file when you have your cursor over FlxButton
.
Now, type the following on the lines before or after super.create();
within the create()
method:
playButton = new FlxButton(0, 0, "Play", clickPlay);
+ add(playButton);
+
+ (import flixel.ui.FlxButton;
)
This creates a FlxButton
object and assigns it to the playButton
variable. We're telling it to make the button at position (0, 0)
(the top left corner of the screen), to make it say "Play" on it, and to call the function clickPlay()
when a user clicks the button (we yet have to implement clickPlay()
).
+ Then we add the object to our state so that it can be shown and interacted with.
+ Don't worry about the position of the button right now, we're going to move it in a second.
Now we need to define our clickPlay()
function. Somewhere in the class, outside of any existing functions, type:
function clickPlay()
+ {
+ FlxG.switchState(new PlayState());
+ }
+
+ (import flixel.FlxG;
)
This function calls FlxG.switchState()
, which switches the state from whatever the current state is (MenuState
) to a new instance of PlayState
.
Technically, at this point, the functionality would work - you could run your game and it would do what we want it to do, but we're missing a few things.
+ First, we want the button to be in a nicer place. Sure, we could set the x
and y
coordinates when we create it, but there's a simpler way to do it.
Back in create()
, add a new line somewhere after we create our FlxButton
, and before super.create();
type:
playButton.screenCenter();
+
+ screenCenter()
is a function which takes an object and centers it on the screen either horizontally, vertically or (by default) both. By calling it on our button, the button will be placed in the center of the screen.
Next, we need to make sure that the game actually starts with our MenuState
. Open Main.hx
and replace the PlayState
reference with MenuState
.
If you test your game out now, it should go straight to a black screen with our 'Play' button in the middle, and clicking the button will take you to another black screen. If that's what you get, it works! So far, so good!
+Next, let's make a simple player sprite that you can move around on the screen.
+First, we need to make a new Player
class. You can think of classes as sort of a functional template. You can use them as-is in a project, but you also have the ability to extend them into new versions. You can kind of think of it as using tracing paper - when you extend a class, you put a new sheet of tracing paper on top - you can still see and use all the stuff in the original class (unless you draw over it), and you can add your own stuff.
We're going to be extending the FlxSprite
class to create our Player
class.
Create another new file in the source
folder, this time called Player.hx
.
Let's create a very basic class which extends FlxSprite
:
package;
+
+ import flixel.FlxSprite;
+
+ class Player extends FlxSprite
+ {
+ public function new(x:Float = 0, y:Float = 0)
+ {
+ super(x, y);
+ }
+ }
+
+ With classes, we use the super
keyword to refer to the parent class. So by calling super(x, y)
within our constructor (called new
), we are basically saying to go up the chain to our parent class, in this case FlxSprite
, and call its constructor, passing it the x
and y
arguments that were passed to us.
Next, we'll want to create a placeholder image to show us where our sprite is, so under super(x, y);
, add:
makeGraphic(16, 16, FlxColor.BLUE);
+
+ (import flixel.util.FlxColor;
)
All we're doing here is saying that we want to make this sprite's graphic be a 16x16 pixel blue square.
+For right now, we just want to get the Player
class initialized, make sure that it works, and try adding it to our PlayState
. It's not going to move or do anything yet, that will come in a minute. So save the changes to our Player
class, and go back to the PlayState
.
+ We need to define our Player
variable, so underneath the class line, add:
var player:Player;
+
+ And in create()
, before super.create();
add:
player = new Player(20, 20);
+ add(player);
+
+ This simply assigns a new instance of our Player
sprite to our player
variable, telling it to be placed at (20, 20)
on the screen, and adds it to our PlayState
.
If you run your project right now, you should see our blue player on the screen!
++
Now let's get it to move around!
+So, how do we actually want our player to move around on the screen? Let's support both arrow keys and WASD keys, in 8 directions: up, down, left, right, and diagonally. The player should move at a relatively fixed speed, but have a slight deceleration before stopping to give it just a little bit of 'feeling'.
+First, define our player's movement speed and deceleration amounts:
+In your player class, above the constructor, add:
+ static inline var SPEED:Float = 100;
+
+ Since this is a constant value that won't change, we make it static inline
as well as following the UPPER_CASE
naming convention.
Then, in the constructor, after you call makeGraphic()
, we need to add some drag
:
drag.x = drag.y = 800;
+
+ drag
, in HaxeFlixel, is sort of a way to slow down an object when it's not being moved. This will prevent our player sprite from just running forever in the same direction when the user stops pressing any movement keys.
+ This is somewhat arbitrary based on what 'feels' right - we can come back and tweak the numbers later on.
While there are plenty of ways to handle player movement, it can be simpler to add it to the Player
class. We'll want to add a new function that will watch for player input and respond to it, so, make a new function:
function updateMovement()
+ {
+ }
+
+First, inside that new function, we want to define some helper variables so we can easily tell which keys were pressed later on in the function:
+ var up:Bool = false;
+ var down:Bool = false;
+ var left:Bool = false;
+ var right:Bool = false;
+
+Next, we want to actually find out which of these directions the player wants to move in. We'll do that by checking whether certain keys are currently being pressed:
+ up = FlxG.keys.anyPressed([UP, W]);
+ down = FlxG.keys.anyPressed([DOWN, S]);
+ left = FlxG.keys.anyPressed([LEFT, A]);
+ right = FlxG.keys.anyPressed([RIGHT, D]);
+
+ (import flixel.FlxG;
)
The anyPressed()
function allows us to ask if any keys out of a list of keys are currently being pressed. You send it an array of keys (their names) and it will return true
if any of them are pressed. There are a couple of similar functions to check for other key states we might use later on.
Next, we want to cancel out opposing directions - if the player is pressing up and down at the same time, we're not going to move anywhere:
+ if (up && down)
+ up = down = false;
+ if (left && right)
+ left = right = false;
+
+Next, we'll want to do something when the player is actually moving:
+ if (up || down || left || right)
+ {
+
+ }
+
+After that, we need to determine which direction to move the player in, and by how much. A common mistake in games that allow diagonal movement is something like this:
+ velocity.x = speed;
+ velocity.y = speed;
+
+ While this will, technically, move something diagonally down and right, it will actually move much FASTER than it really should be moving. This is because of the way triangles work. So, for our player to move, we're not just going to set its velocity
to speed
- that would be too easy! Instead, we're going to calculate exactly what its velocity should be with angles!
The first part of this is to figure out what angle we want to have the player move based on the keys that are being pressed. With our player sprite's asset, angle 0 is to the right, and -90 (or 270) is up.
+ var newAngle:Float = 0;
+ if (up)
+ {
+ newAngle = -90;
+ if (left)
+ newAngle -= 45;
+ else if (right)
+ newAngle += 45;
+ }
+ else if (down)
+ {
+ newAngle = 90;
+ if (left)
+ newAngle += 45;
+ else if (right)
+ newAngle -= 45;
+ }
+ else if (left)
+ newAngle = 180;
+ else if (right)
+ newAngle = 0;
+
+All this will do is create a temporary variable to hold our angle, and then, based on which direction(s) the player is pressing, set that angle to the direction we plan on moving the player.
+Now that we know which angle the player should be moving in, we need to figure out with how much velocity
it needs to move that way. We're going to do this by setting velocity.x
to speed
and velocity.y
to 0
. Then we rotate that point around (0, 0)
by newAngle
degrees.
velocity.setPolarDegrees(SPEED, newAngle);
+
+ (import flixel.math.FlxPoint;
)
...and that's the end of our updateMovement()
function!
The only thing left to do is to override
the update()
function in Player.hx
as well and call updateMovement()
from it. VSCode can generate the necessary boilerplate code for you, just type override
and a space, after which a completion popup should appear:
+
Select update
and press enter.
Now you just need to add the function call, after which it should look like this:
+ override function update(elapsed:Float)
+ {
+ updateMovement();
+ super.update(elapsed);
+ }
+
+ The update()
function, as you should remember, is called each 'frame' of the game. So, each time our PlayState
gets its update()
called, it calls update()
on all of its members
, including the player's update()
. This in turn will run our updateMovement()
logic and adjust the player's velocity
accordingly. After that, it call its super.update()
, which will take the velocity
that we've just changed into account and figures out where the player sprite should move to.
Whew! It sounds a lot more complicated than it really is - if you try out the game right now, you'll see that you can run around the screen by pressing any combination of arrow keys and WASD!
+Next, we'll work on making the player sprite actually look like something!
+ + +Conditional compilation flags are a powerful feature Haxe offers to optimize your code by only compiling certain parts of it. For example, the following pseudo-code optimizes inputs based on the target platform:
+#if (web || desktop)
+keyboardControls();
+mouseControls();
+#end
+
+#if desktop
+gamepadControls();
+#end
+
+#if mobile
+touchControls();
+#end
+
+Basically, this means that for web or desktop targets, we want to have mouse and keyboard controls. In case of a mobile target, we want touch input. Like you can see, these so-called defines can also be nested: We only want gamepad controls to be available on non-web targets (line 4). So, all in all, these defines can be used largely like traditional if-statements, except their syntax varies slightly.
+For more information on what defines are available, by default check out the OpenFL documentation on this topic.
+If you had a look at the OpenFL documentation, you might have found out that you can also set your own defines. When working with OpenFL, it's convenient to do so in the Project XML file. +HaxeFlixel makes use of this feature and allows you to optimize your game using the following defines (a list of them can also be found in the template):
+// Example xml node to enable to Flixel record system.
+<haxedef name="FLX_RECORD" />
+
Flixel features a powerful recording / playback feature which captures mouse and keyboard input. Since it's used rarely, we decided that it makes sense to turn it off by default. Setting this define however will enable it again.
+This define is required for middle and right mouse button input. On the flash target, a minimum flash player version of 11.2 is required. Listening to right click input will also disable the right-click menu of the flash player. There is currently no HTML5 support for this feature.
+By default, flixel uses the flash native cursor API. This gets rid of the almost unbearable mouse lag that is unavoidable otherwise. This define allows you to disable that - reasons for this might be that you want to target a flash player version older than 10.2 or that you need to use a cursor bigger than 32x32, which is not possible using the native cursor API.
+This allows you to optimize your game by compiling it without any mouse-related code, which can make sense for mobile targets. This is why in the template, if="mobile"
is added to the haxedef set tag. Keep in mind that this might require you to use conditionals in your own code for anything related to FlxG.mouse
, which does not exist if this define is set.
This allows you to optimize your game by compiling it without any keyboard-related code, which can make sense for mobile targets. This is why in the template, if="mobile"
is added to the haxedef set tag. Keep in mind that this might require you to use conditionals in your own code for anything related to FlxG.keyboard
or FlxG.keys
, which do not exist if this define is set.
This allows you to optimize your game by compiling it without any touch-related code, which can make sense for desktop targets. This is why in the template, if="desktop"
is added to the haxedef set tag. Keep in mind that this might require you to use conditionals in your own code for anything related to FlxG.touches
, which does not exist if this define is set.
This allows you to optimize your game by compiling it without any gamepad-related code. Keep in mind that this might require you to use conditionals in your own code for anything related to FlxG.gamepads
, which does not exist if this define is set.
This disables the flixel core sound tray, which appears whenever your adjust the volume via the 0 (mute the game), + or - keys. Note that this does not disable the ability to control the game's volume using these hotkeys, just the soundtray itself. You can disable volume control entirely by setting FlxG.sound.muteKeys
, FlxG.sound.volumeUpKeys
and FlxG.sound.volumeDownKeys
to null
.
Flixel automatically pauses the game when it loses focus and shows its so-called "focus lost screen", which is basically an overlay darkening the screen area with a white "play-button" in the middle. Using this define disables that screen, but not the feature to automatically pause - use the FlxG.autoPause
Bool for that.
This disables the flixel core debugger, which you can toggle using the \ and ` keys. It makes sense to do so for release build, which is why unless="debug"
is added to this tag in the template. It basically means that the flixel debugger is disabled when compiling a release, and enabled when compiling a debug build.
Welcome to the HaxeFlixel handbook. These pages are intended to answer common questions and explain the main ideas and structures behind most HaxeFlixel games. +As the types of games and use cases of HaxeFlixel are vast we appreciate developers making additions and improvements to this documentation through GitHub.
+If you are familiar with AS3 and new to Haxe, we encourage you to read the AS3 and Haxe comparison.
+HaxeFlixel's API is similar to the AS3 version, so existing documentation and resources for ActionScript 3 are still relevant.
+ + +HaxeFlixel currently supports the following targets through OpenFL:
+Using these targets requires the appropriate SDK's and system setup assisted through the Lime setup commands.
+lime setup windows
+lime setup mac
+lime setup android
+
"Hello World" is a common example of getting started with programming, here we will show you how to add Hello World with HaxeFlixel's FlxText
. You can compile this test for any supported target platform.
Make sure you have installed Flixel, as well as set up the lime
and flixel
commands as explained previously.
HaxeFlixel requires a basic structure of files for any project that can be created automatically for you. First open a command prompt and switch to the directory where you'd like to have the project using cd
, for example:
cd C:\Users\<User>\Projects
+
+Then run the template
command (tpl
for short):
flixel tpl -n "HelloWorld"
+
+You will now see a new folder named "HelloWorld" with all the files for your project being created automatically.
+FlxText
Adding the text is as simple as opening the PlayState.hx
file in the newly created source
folder. The file should look like this:
package;
+
+import flixel.FlxState;
+
+class PlayState extends FlxState
+{
+ override public function create()
+ {
+ super.create();
+ }
+
+ override public function update(elapsed:Float)
+ {
+ super.update(elapsed);
+ }
+}
+
+All you need to do is add the following three lines to the create()
function (and save the file):
override public function create()
+{
+ super.create();
+
+ var text = new flixel.text.FlxText(0, 0, 0, "Hello World", 64);
+ text.screenCenter();
+ add(text);
+}
+
+This will create a new FlxText
instance with font size 64
, center it on the screen and add()
it so it is displayed.
Return to your command line window - now we can compile the project. First switch to the directory containing the Project.xml
file:
cd HelloWorld
+
+You can then compile to HTML5, Neko and Flash out of the box with these commands:
+lime test html5
+lime test neko
+lime test flash
+
+Other targets require further setup. For more details, check the "Advanced Setup" section of the Lime documentation.
+If you struggled through any part of this tutorial, get in touch with the community for support.
+ + + +The HaxeFlixel documentation is provided from the flixel-docs repository and is open for contributions on GitHub that will be pushed here.
+ +Developers may want to contribute and / or stay updated with the bleeding edge code of HaxeFlixel. Like any development code we caution the use of it as it may not have the same amount of testing as the stable version.
+All new development is done on the dev branches of HaxeFlixel's GitHub repositories.
+Prerequisites
+You must have completed the Getting Started guide to have the necessary dependencies (Haxe and OpenFL).
+If you want to use development builds of OpenFL and Lime, please refer to these instructions:
+ +Install Git
+Windows-only note: ensure that during the installation process, you select the non-default option that allows you to use Git from the Windows command prompt (you'll also need to re-open your command prompt window after installation for Git to become available there).
+Install Flixel from GitHub
+To obtain the newest version, please input the following command into your console:
+haxelib git flixel https://github.com/HaxeFlixel/flixel
+
+The same applies for any additional Flixel libraries:
+haxelib git flixel-demos https://github.com/HaxeFlixel/flixel-demos
+haxelib git flixel-addons https://github.com/HaxeFlixel/flixel-addons
+haxelib git flixel-ui https://github.com/HaxeFlixel/flixel-ui
+
+Should you want to use haxelib set <library> <version>
to switch back to released versions, be sure to run haxelib dev <library>
beforehand. haxelib git
enables the "development directory" for a library, which overrides haxelib set
commands.
Testing the installation with the Mode demo
+Navigate to flixel-demos/Platformers/Mode
. Compile and run the game with lime test <target>
.
Need more help?
+Get in touch with the community.
+ + +To install the latest stable version of HaxeFlixel, open a command prompt and run the following Haxelib commands:
+haxelib install lime
+haxelib install openfl
+haxelib install flixel
+
+After the installation is complete, you can compile games to HTML5, Flash and Neko out of the box.
+To easily install additional libraries (addons, ui, demos, tools, templates...) in a single step, just run:
+haxelib run lime setup flixel
+
+lime
commandhaxelib run lime setup
+
+This makes lime
available as a command (alias for haxelib run lime
).
To compile to desktop and mobile targets, you have to make sure you have run the respective lime setup
+commands. Each are specified in the
+Lime "Advanced Setup" docs.
flixel
commandRun the following two commands to install flixel-tools (needed for project templates among other things):
+haxelib install flixel-tools
+haxelib run flixel-tools setup
+
+If a new version of Flixel has been released, and you want to update to it, you can use the following command to do so:
+haxelib update flixel
+
+If you wanted to update e.g. flixel-addons
instead, just replace flixel
with flixel-addons
.
To stay informed about new releases, you can follow @HaxeFlixel on Twitter or check out our Blog from time to time.
+If you are interested in using bleeding edge code from the development branch on GitHub, see instructions here.
+ + +Haxe is a cross-platform toolkit for developing applications, games, multimedia and server side code natively. Cross-compiling your code offers longevity to your code base that is not possible for other methods of cross-platform development.
+The Haxe programming language itself is high level and strictly typed. It allows for Object Orientated programming with similar to syntax in AS3 and Java. Haxe is often learnt quickly and loved by developers coming from similar languages. Haxe is a valuable language to learn not only for games, but server-side development, command line tools and various other technologies.
+Haxe is an open source technology, free to use and modify. The Haxe Toolkit provides a powerful cross-platform standard library that lets your code solve problems and have a codebase that is more versatile.
+Backed by the Haxe Foundation, industry sponsors and partners, it receives enterprise support and is sponsored and recognized by developers at a growing number of large companies such as Nickelodeon, Disney, TiVo, Stencyl and more.
+Haxe is being developed on GitHub:
+ + + + +The Open Flash Library (OpenFL) previously known as NME, is an innovative framework designed to provide fast, productive development for Windows, Mac, Linux, iOS, Android, Flash and HTML5 – all using the same source code.
+OpenFL has a history of providing the Flash API wherever possible however it is also used to extend upon that API. OpenFL is powered by the Haxe Toolkit's cross-compiler that lets it produce native code such as C++ for its target platforms.
+OpenFL has a an active community of developers building games for the web, consoles, desktop and mobile devices. OpenFL is free to use and modify and it is currently being developed openly on GitHub. The project made from the combined work of talented and passionate developers over many years has resulted in a mature and advanced platform.
+OpenFL targets include native cross-compiled C++ for desktop and mobile targets, as well as web targets such as Flash, HTML5 and experimental support for Emscripten. OpenFL is written primarily in the Haxe language as well as platform specific code integrating with SDKs and native APIs.
+OpenFL provides HaxeFlixel with a familiar Flash API as well as an extended set of features for native targets. This includes the use of GPU accelerated texture batching through drawTiles, multi-threading and more.
+ + +The iOS target makes use of a chain of frameworks to compile your native iOS game from Haxe code. OpenFL uses the Hxcpp and XCode natively so no virtual machine is involved. +When you compile an iOS project an XCode project file is also automatically generated in the build directly so you can make use of the XCode profiler and toolset.
+The Haxe compiler uses it's cpp
target to compile your Haxe
code for the LibSDL OpenGL library.
+iOS is is part of the cpp group of targets so when developers mention cpp
the topic will may be relevant to HaxeFlixel iOS.
With OpenFL using native-code and OpenGL with LibSDL, the rendering methods are different to where Flixel started with Flash. +iOS uses GPU accelerated Texture Batching for the best possible performance in mobile devices.
+#if cpp
+//your iOS code
+#end
+
+#if ios
+//your iOS code
+#end
+
+#if mobile
+//your iOS code
+#end
+
Mobile platforms can use a window width
and height
of 0, which is a special value that uses the full resolution of the current display.
<window width="0" height="0" background="#FFFFFF" fps="60" />
+
OpenFL also exposes the following specific settings for the iOS target:
+<window hardware="true" allow-shaders="true" require-shaders="true" if="cpp"/>
+<window vsync="true" antialiasing="4" if="cpp" />
+<window orientation="portrait" /> || <window orientation="landscape" if="cpp"/>
+
Custom PNG icons and launch images: (Check iOS Icon and Image Sizes Guidelines for more info)
+<set name="PRERENDERED_ICON" value="true" />
+
+<icon path="Icon.png" size="57" if="ios" />
+<icon path="Icon@2x.png" size="114" if="ios" />
+<icon path="Icon-72.png" size="72" if="ios" />
+<icon path="Icon-72@2x.png" size="144" if="ios" />
+
+<launchImage path="Default.png" width="320" height="480" if="ios" />
+<launchImage path="Default@2x.png" width="640" height="960" />
+<launchImage path="Default-Portrait~ipad.png" width="768" height="1024" if="ios" />
+<launchImage path="Default-Portrait@2x~ipad.png" width="1536" height="2048" if="ios" />
+<launchImage path="Default-Landscape~ipad.png" width="1024" height="768" if="ios" />
+<launchImage path="Default-Landscape@2x~ipad.png" width="2048" height="1536" if="ios" />
+<launchImage path="Default-568h@2x.png" width="640" height="1136" if="ios" />
+
Visual Studio Code, FlashDevelop and IntelliJ IDEA support iOS compilation through their GUI.
+The basic command to compile and test iOS:
+lime test ios
+
Run this command from the root folder of your project, the default Project.xml
will be used automatically.
If you want to use the iOS simulator, add -simulator
when running/testing.
lime test ios -simulator
+
+
+ Keyboard input for HaxeFlixel is provided through the FlxKeyboard
class and is available through FlxG.keys
.
An object of type FlxKeyList
contains a Bool
for each key on the keyboard. The values are kept as an array internally, but you can easily refer to them by using special instance variables named after the key. For a full list of all available names look at the FlxKeyList
documentation here.
FlxKeyboard
uses the three key lists pressed
, justPressed
and justReleased
to keep track of the keys. pressed
contains true
for all keys that are currently pressed. justPressed
only contains true
for all keys that have been pressed since the last frame.
Here's an example of how to put it all together:
+override public function update(elapsed:Float):Void
+{
+ if (FlxG.keys.pressed.UP)
+ {
+ // The up arrow key is currently pressed
+ // This code is executed every frame, while the key is pressed
+ }
+
+ if (FlxG.keys.justPressed.LEFT)
+ {
+ // The left arrow key has just been pressed
+ // This code is only executed once, on the frame immediately after the key has been pressed
+ }
+
+ if (FlxG.keys.justReleased.LEFT)
+ {
+ // The left arrow key has just been released
+ // This code is only executed once, on the frame immediately after the key has been released
+ }
+
+ super.update(elapsed);
+}
+
+You can check multiple keys at once using the anyPressed()
, anyJustPressed()
and anyJustReleased()
methods of FlxKeyboard
. This allows you to bind multiple keys to one action easily, for example controlling the player with either WASD or the arrow keys. These methods take an Array
of key names (as String
s) and return true if any of the given keys have the desired property.
override public function update(elapsed:Float):Void
+{
+ if (FlxG.keys.anyPressed([LEFT, A]))
+ {
+ // Move left
+ }
+
+ if (FlxG.keys.anyPressed([RIGHT, D]))
+ {
+ // Move right
+ }
+
+ super.update(elapsed);
+}
+
+For general information on conditionals, please refer to this page.
+FLX_NO_KEYBOARD
This can be used to remove all keyboard-related logic from HaxeFlixel for optimization purposes, which can make sense on mobile targets, which is why it is combined with if="mobile"
in the default Project.xml
.
One of the great things about using Ogmo with HaxeFlixel is that there is already a built-in class to load and use the maps. However, it's not in the 'standard' HaxeFlixel library - we have to install the flixel-addons library to get access to it.
+Open up a new command prompt and enter:
+ haxelib install flixel-addons
+
+This should run and install the new library. Close the command prompt window when it's finished.
+Jump back into VSCode and open up your Project.xml
file. We need to tell our project to include flixel-addons in the libraries.
Look for a line that says:
+ <!--<haxelib name="flixel-addons" />-->
+
+and change it to:
+ <haxelib name="flixel-addons" />
+
+Save this change - you're now playing with flixel-addons!
+Go back to your PlayState
, and, underneath where the player
variable is defined, add:
var map:FlxOgmo3Loader;
+ var walls:FlxTilemap;
+
+ We're basically just creating an object to hold our Ogmo map, and then another one to hold the FlxTilemap
that we will generate from the Ogmo map.
In create()
, before we setup the player object, add:
map = new FlxOgmo3Loader(AssetPaths.turnBasedRPG__ogmo, AssetPaths.room_001__json);
+ walls = map.loadTilemap(AssetPaths.tiles__png, "walls");
+ walls.follow();
+ walls.setTileProperties(1, NONE);
+ walls.setTileProperties(2, ANY);
+ add(walls);
+
+ This just loads our room file into our FlxOgmo3Loader
object, generates our FlxTilemap
from the 'walls' layer, and then sets tile 1 (our floor tile) to not collide, and tile 2 (walls) to collide from any direction. Then we add our tilemap to the state.
Now, we need to make our player object get placed in the right location on the map. So, change where we initialize our player from:
+ player = new Player(20, 20);
+
+to:
+ player = new Player();
+ map.loadEntities(placeEntities, "entities");
+
+ We're simply telling our map
object to loop through all of the entities in our 'entities' layer, and call the placeEntities()
for each one (which we're about to make now).
Let's make the placeEntities()
function now. When we call loadEntities()
on our map, it will pass the data of all of the placed entities to whatever function we want. In our function, we need to take this information and do something with it. It will look like this now:
function placeEntities(entity:EntityData)
+ {
+ if (entity.name == "player")
+ {
+ player.setPosition(entity.x, entity.y);
+ }
+ }
+
+ So, if this function gets passed an entity with the name "player", it will set our player object's x
and y
values to the entity's x
and y
values.
Now, we want to add collisions to our state, so the player will bump into walls instead of just walking through them. So, in update()
, after super.update(elapsed);
add:
FlxG.collide(player, walls);
+
+ All this does is check for overlaps between our player and the walls tilemap each update()
call. If there are any overlaps, the objects are automatically separated from each other.
Finally, we want to make a small tweak to the player sprite. It's a good idea to make sure that your player has a decent chance of making it through doorways. Since by default, our player sprite is the same size as our tiles (16x16 pixels), it makes it so the player has to thread the needle to make it through 1-tile wide doorways. To remedy this, we're going to change the player sprite's size and offsets. This won't change what is actually displayed for the player's graphic, only its hitbox.
+ So, in the Player
class, in the constructor, under where we set the drag
, add:
setSize(8, 8);
+ offset.set(4, 4);
+
+Since we just changed the player's hitbox, we want to visualize it! Switch to HTML5 / Debug
in the status bar and build the project. You can now press F2
to bring up HaxeFlixel's powerful debugging overlay.
Press the 3D-ish looking cube button in the upper right corner to render hitboxes:
+ +If you look closely, you can see that the player's is smaller than the tiles now.
+In the next part, we'll talk about some small tweaks to the camera.
+ + +With the penetration of mobile devices in the world ignoring mobile targets for your games and applications is ignoring a huge audience.
+Mobile devices even though amazing have particular bottlenecks not present on desktop targets. The bottleneck most significant to HaxeFlixel games is the low powered CPU. HaxeFlixel's answer to this is to make use of GPU acceleration provided by OpenFL.
+The processing power of mobile CPUs are often the biggest bottleneck when compared to a desktop target. Mobile CPUs are constrained by low power architectures and are simply not as powerful. Manipulating pixels like bitmap filters do with Flash software rendering is simply too slow for mobile devices. HaxeFlixel has answered this for Flixel games to use the GPU for rendering your sprites instead of using software rendering like its done with Flash.
+Memory on mobile devices as you can imagine does not compare to the desktop. When we talk about memory it relates to how many textures your game can store to render as well as the amount of raw data and objects in your game's runtime state. Memory lets you cache objects for quick runtime access instead of creating objects during your gameplay which may cause skipping and low performance. This is why HaxeFlixel uses destroy method and has implemented support for texture atlas'.
+Due to the nature of mobile hardware there are often maximum sizes for textures that are much lower than desktop hardware. There is no definitive guide to size limits as it depends on the hardware of each device individually.
+Here you can see an overview of size limits obtained from WebGL browsers http://webglstats.com/#h_texsize
+You can also look up a device on glxbench.com, under the GL config tab look for GL_MAX_TEXTURE_SIZE
.
Here is an overview of the variety in devices, feel free to add to the list;
+Device | +Processor | +Max Texture Size | +Display Resolution | +Memory | +
+ +iPhone + + | ++ +Armv6 + + | ++ +1024x1024 + + | ++ +480x320 + + | ++ +128MB + + | +
+ +iPhone 3G + + | ++ +Armv6 + + | ++ +1024x1024 + + | ++ +480x320 + + | ++ +128MB + + | +
+ +iPhone 3GS + + | ++ +Cortex-A8 + + | ++ +2048x2048 + + | ++ +480x320 + + | ++ +256MB + + | +
+ +iPhone 4 + + | ++ +Cortex-A8 + + | ++ +2048x2048 + + | ++ +960x640 + + | ++ +512MB + + | +
+ +iPhone 4S + + | ++ +Cortex-A9 Dual-Core + + | ++ +4096x4096 + + | ++ +960x640 + + | ++ +512MB + + | +
+ +iPad + + | ++ +Cortex-A8 + + | ++ +2048x2048 + + | ++ +1024x768 + + | ++ +512MB + + | +
+ +iPad 2 + + | ++ +Cortex-A9 Dual-Core + + | ++ +2048x2048 + +4096x4096 (iOS 5.1) + + | ++ +1024x768 + + | ++ +512MB + + | +
iPad 3 | +Cortex-A9 Dual-Core | +4096x4096 | +2048x1536 | +1024MB | +
Mouse input for HaxeFlixel is provided through the FlxMouse
class and is available through FlxG.mouse
. It is worth noting that it extends FlxPoint
.
On non-mobile targets, the mouse starts out by being visible by default. You can set the visibility via FlxG.mouse.visible
.
The most common use of FlxG.mouse
is checking the state of the left mouse button.
override public function update(elapsed:Float):Void
+{
+ if (FlxG.mouse.pressed)
+ {
+ // The left mouse button is currently pressed
+ }
+
+ if (FlxG.mouse.justPressed)
+ {
+ // The left mouse button has just been pressed
+ }
+
+ if (FlxG.mouse.justReleased)
+ {
+ // The left mouse button has just been released
+ }
+
+ super.update(elapsed);
+}
+
+
+Exactly the same logic can be used for the middle and right mouse buttons. The properties are postfixed by Middle
/ Right
, e.g. FlxG.mouse.pressedRight
.
import flixel.util.FlxColor;
+using flixel.util.FlxSpriteUtil;
+
+// Create a white circle to use as a cursor graphic
+var sprite = new FlxSprite();
+sprite.makeGraphic(15, 15, FlxColor.TRANSPARENT);
+sprite.drawCircle();
+
+// Load the sprite's graphic to the cursor
+FlxG.mouse.load(sprite.pixels);
+
+// Use the default mouse cursor again
+FlxG.mouse.unload();
+
+// To use the system cursor:
+FlxG.mouse.useSystemCursor = true;
+
+There are two kinds of position data available:
+ FlxG.mouse.x
+ FlxG.mouse.y
+ FlxG.mouse.getWorldPosition(); // returns x / y as a FlxPoint
+
+ FlxG.mouse.screenX
+ FlxG.mouse.screenY
+ FlxG.mouse.getScreenPosition(); // returns screenX / screenY as a FlxPoint
+
+The current "delta" value of mouse wheel is provided in a simple Int
property. If the wheel has just been scrolled up, it will have a positive value and vice versa. If it wasn't scrolled during the current frame, it's 0.
if (FlxG.mouse.wheel != 0)
+{
+ // Mouse wheel logic goes here, for example zooming in / out:
+ FlxG.camera.zoom += (FlxG.mouse.wheel / 10);
+}
+
+AS3-Flixel used a simple Sprite
to display a bitmap graphic that is synchronized with the mouse position every frame. This approach is not optimal, as it causes what is commonly referred to as "mouse lag" - the cursor sprite lags behind the actual mouse position. Depending on the game's framerate and the player's mouse speed, this can be very noticeable and thus have a negative impact on the overall experience.
HaxeFlixel leverages the flash native cursor API for better mouse cursor performance. However, there are certain restrictions to native cursors:
+Because of this, using the native cursor API is completely optional, see the section on the FLX_NO_NATIVE_CURSOR
conditional.
You can use FlxG.mouse.load()
for native cursors as usual, but if you want more fine-grained control, you can use the following functions:
FlxG.mouse.setSimpleNativeCursorData("custom", BitmapData);
+FlxG.mouse.registerNativeCursor("custom", MouseCursorData);
+
+You can find more information on the MouseCursorData
object here.
Use FlxG.mouse.setNativeCursor()
to switch to an already registered native cursor.
For general information on conditionals, please refer to this page.
+FLX_NO_MOUSE_ADVANCED
By default, there are event listeners set up for both the middle and the right mouse button. There are two reasons for wanting to disable this:
+FLX_NO_NATIVE_CURSOR
This disables the native cursor API on the flash target. For more info, check the "Flash native cursor API"-section above.
+FLX_NO_MOUSE
This can be used to remove all mouse-cursor-related logic from HaxeFlixel for optimization purposes, which can make sense on mobile targets, which is why it makes sense to combine this with if="mobile"
in your Project.xml
.
One of the big advantages of using HaxeFlixel is the ability to build your games for multiple platforms. You can build a working HTML5, Windows, Linux, Mac, Android and iOS game - all from the same code! So far, we've been working with HTML5, and, for the most part, you don't have to do too much to get your game working on other platforms - if you test it under Windows right now, it should mostly work just fine (although, without music, which we'll discuss later). However, you might run into some issues if you try to build it for a mobile device - at the very least, you won't be able to play it without a keyboard. We'll focus on Windows and Android in this tutorial and add a few things to make them work better on those platforms.
+HaxeFlixel comes equipped with a powerful feature called conditionals. By adding some conditionals to your code, you can make it so that only certain pieces of code will be compiled depending on the conditionals you use. For example, if you are building for Android, which would probably not have a mouse, you could setup a conditional to skip over all mouse-related code. Alternately, you might also add some code to deal with touches, which would only work on mobile platforms, so you would make sure to wrap that logic in conditionals so that it doesn't try to build on Windows or HTML5.
+It's not all that complicated. We'll start with something simple: adding the ability to toggle fullscreen mode on Windows.
+Let's add a button to the OptionsState
:
When we initialize and interact with our button, we only want to do it on desktop
platforms. So, at the top of the class, add:
#if desktop
+ var fullscreenButton:FlxButton;
+ #end
+
+In create()
, somewhere after we add our volumeBar
object, add:
#if desktop
+ fullscreenButton = new FlxButton(0, volumeBar.y + volumeBar.height + 8, FlxG.fullscreen ? "FULLSCREEN" : "WINDOWED", clickFullscreen);
+ fullscreenButton.screenCenter(X);
+ add(fullscreenButton);
+ #end
+
+Then we need to add our callback function for that button:
+ #if desktop
+ function clickFullscreen()
+ {
+ FlxG.fullscreen = !FlxG.fullscreen;
+ fullscreenButton.text = FlxG.fullscreen ? "FULLSCREEN" : "WINDOWED";
+ save.data.fullscreen = FlxG.fullscreen;
+ }
+ #end
+
+ Yes, we wrap the entire function in our conditional - if we're not building for desktop
, this function will not exist. You'll also notice that we are saving the value of FlxG.fullscreen
- this will be used to 'remember' the game's screen state when they next launch the game.
Next, let's change around our Main.hx
so that when the game starts it will launch into whatever state the game was last in - fullscreen or not. We need to move things around a little, since we need to find out if we're going into fullscreen or not BEFORE we call new FlxGame()
, but we need to set the volume AFTER, so new()
should look like this:
var startFullscreen:Bool = false;
+ var save:FlxSave = new FlxSave();
+ save.bind("TurnBasedRPG");
+ #if desktop
+ if (save.data.fullscreen != null)
+ {
+ startFullscreen = save.data.fullscreen;
+ }
+ #end
+
+ super();
+ addChild(new FlxGame(320, 240, MenuState, 1, 60, 60, false, startFullscreen));
+
+ if (save.data.volume != null)
+ {
+ FlxG.sound.volume = save.data.volume;
+ }
+ save.close();
+
+Next, we need to give players a way to exit the game if they're in fullscreen mode - since they won't easily have access to the close-button anymore.
+We do that by adding a new button to the MenuState
:
#if desktop
+ var exitButton:FlxButton;
+ #end
+
+ And in create()
:
#if desktop
+ exitButton = new FlxButton(FlxG.width - 28, 8, "X", clickExit);
+ exitButton.loadGraphic(AssetPaths.button__png, true, 20, 20);
+ add(exitButton);
+ #end
+
+Then our callback function just looks like:
+ #if desktop
+ function clickExit()
+ {
+ Sys.exit(0);
+ }
+ #end
+
+There you go! Now try it out on both HTML5 and Windows targets and notice the differences - you can't go fullscreen in HTML5, and you can in Windows. You also get the button to exit the game on the MenuState while on Windows.
+To make our game work with Android, we have to make a few more changes. First, we need to make sure that our Project.xml
is setup correctly so that our settings are right for Android.
In your app tag, at the very top of the file, add this attribute:
+ package="com.haxeflixel.turnBasedRPG"
+
+This will give your app a package name to be used on the Android device - it's best to be as unique as possible.
+You might be wondering how we're going to allow the user to move around in our game on a device without the keyboard or mouse. Well, HaxeFlixel comes with a FlxVirtualPad
class which we can use to accept touch input from the user and translate it into movement. Head over to PlayState.hx
:
Define:
+ #if mobile
+ public static var virtualPad:FlxVirtualPad;
+ #end
+
+Create and add (in create):
+ #if mobile
+ virtualPad = new FlxVirtualPad(FULL, NONE);
+ add(virtualPad);
+ #end
+
+Since the player can't move while they're in combat, we're going to remove the distraction of our virtual pad while combat is happening. In the startCombat()
function, add:
#if mobile
+ virtualPad.visible = false;
+ #end
+
+ And, to make it reappear, in update()
, somewhere around where we set inCombat
to false
, add:
#if mobile
+ virtualPad.visible = true;
+ #end
+
+The only place where we actually check for button presses is in our Player
class, in the updateMovement()
function. We currently have a section of code that is setting different gs to true based on keyboard input - but since the keyboard might be disabled on some platforms, we need to change that to look like this:
#if FLX_KEYBOARD
+ up = FlxG.keys.anyPressed([UP, W]);
+ down = FlxG.keys.anyPressed([DOWN, S]);
+ left = FlxG.keys.anyPressed([LEFT, A]);
+ right = FlxG.keys.anyPressed([RIGHT, D]);
+ #end
+
+Right after that logic, we'll add our logic that checks each of the buttons of our virtual pad to see which ones are pressed:
+ #if mobile
+ var virtualPad = PlayState.virtualPad;
+ up = up || virtualPad.buttonUp.pressed;
+ down = down || virtualPad.buttonDown.pressed;
+ left = left || virtualPad.buttonLeft.pressed;
+ right = right || virtualPad.buttonRight.pressed;
+ #end
+
+ Because of the way we've structured our movement logic, we don't need to make any other changes! Getting up
set to true via keyboard or our virtual pad will do the same thing.
Things are a little different for our combat screen. We could setup the virtual pad to work with our combat menu, but that would feel a little clunky, given that the user has the power to tap on whatever they want on the screen. Instead, we're going to utilize touches to figure out what the player wants to do.
+ This means, in our update function, we need to wrap everything that's currently inside updateKeyboardInput()
with:
#if FLX_KEYBOARD
+ ...
+ #end
+
+Inside of our if (!wait)
statement, add:
updateTouchInput();
+
+And
+ function updateTouchInput()
+ {
+ #if FLX_TOUCH
+ for (touch in FlxG.touches.justReleased())
+ {
+ for (choice in choices.keys())
+ {
+ var text = choices[choice];
+ if (touch.overlaps(text))
+ {
+ selectSound.play();
+ selected = choice;
+ movePointer();
+ makeChoice();
+ return;
+ }
+ }
+ }
+ #end
+ }
+
+What this does is gets a list of all the touches that were just released this update and checks them one at a time to see if any were on top of one of our choices texts. If one of them is, we play our select sound, and act on the selection - ignoring any other touches that might have happened this update.
+And that's everything! You should be able to run your game on HTML5, Windows (Neko, too!), and Android and have it work on each with slight variations. And you only have one project and one codebase!
+Next we'll polish up our game and add a little juice to make it POP!
+ + +Neko is a high-level dynamically typed programming language and virtual machine. Neko as a target of HaxeFlixel and OpenFL supports an API close to the CPP builds of HaxeFlixel.
+The NekoVM itself is part of the Haxe Toolkit and is used throughout Haxelib, Lime, OpenFL and flixel-tools to easily create cross-platform tools. Neko gives the power of the Haxe standard library to cross platform development tools.
+Neko itself is also used server-side in websites and general backend systems like the Haxelib. It contains a fully featured threading and socket API as well as integration with the Apache server.
+ +++"When NME (Now known as Lime/OpenFL) was first created, it was designed to provide graphics, sound and other media for the NekoVM, but since the performance was not yet ideal, Hugh then decided (if I have my order of events correct) to experiment with a C++ target for Haxe. It worked, and HXCPP was born."
+
Its advantages:
+Its disadvantages:
+#if neko
+//your neko code
+#end
+
+#if desktop
+//your neko code
+#end
+
The basic command to compile and test the Neko target:
+lime test neko
+
+
+ Every OpenFL project uses an XML file to setup the compile settings for your projects depending on your chosen target. This includes stage size, source paths, asset paths and more.
+Click here to see the Project.xml from the default HaxeFlixel template.
+This is a complete specification of the XML file:
+ + + +Now that we have our little guy running around our map, lets give him something to pick up. We'll add some simple coins that will add to the player's score when they are picked up.
+Open up your project in Ogmo again, and click Edit Project
.
On the Entities
tab, add a new entity:
+
Open up the level we used before, and, on the 'entities' layer, scatter a bunch of coins around:
++
We're going to make our coins be 8x8 pixels. For the coin's graphic, you can use this image , or make your own. Make sure you save this in assets/images
.
Get back into VSCode, and make a new Coin
class:
package;
+
+ import flixel.FlxSprite;
+
+ class Coin extends FlxSprite
+ {
+ public function new(x:Float, y:Float)
+ {
+ super(x, y);
+ loadGraphic(AssetPaths.coin__png, false, 8, 8);
+ }
+ }
+
+Now, head back to the PlayState
. We need to change our map logic so that when it's loading the entities and sees a coin in our Ogmo file, it will add a Coin
object to our state.
First, let's make a group to hold all the coins in. At the top of our class, where we defined all our variables so far, add:
+ var coins:FlxTypedGroup<Coin>;
+
+ Groups are like arrays of Flixel objects which can be used in a lot of different ways. In this case, since our group will only be containing coins, we will make it a FlxTypedGroup<Coin>
.
In create()
, after we add our walls, and before we initialize our player, we need to initialize and add our coin group:
coins = new FlxTypedGroup<Coin>();
+ add(coins);
+
+Next, we just want to change our placeEntities()
function to put a coin into our group every time it encounters one in our Ogmo file. At the end of our if statement, add:
else if (entity.name == "coin")
+ {
+ coins.add(new Coin(entity.x + 4, entity.y + 4));
+ }
+
+ This will simply create a new coin, tell it to be at the position defined in the Ogmo file (+4
to x
and y
to center it on the tile), and add it to the coin group.
Now we need to have the player be able to collect the coins. We're going to use an overlap check to do this. In update()
, after your FlxG.collide()
call, add:
FlxG.overlap(player, coins, playerTouchCoin);
+
+This just says: every frame, check if there are any overlaps between the player and the coin group, and if there are, call playerTouchCoin
.
Let's add the playerTouchCoin()
callback now:
function playerTouchCoin(player:Player, coin:Coin)
+{
+ if (player.alive && player.exists && coin.alive && coin.exists)
+ {
+ coin.kill();
+ }
+}
+
+This function simply verifies that the player and the coin that overlap each other are both alive and exist. If so, the coin is killed (we'll add the score a little later on).
+If you run the game right now, as you walk around the map, each coin you touch will disappear. Works great, but it's a little boring… Let's add a little style!
+Go back to your Coin
class, and add these functions:
override function kill()
+{
+ alive = false;
+ FlxTween.tween(this, {alpha: 0, y: y - 16}, 0.33, {ease: FlxEase.circOut, onComplete: finishKill});
+}
+
+function finishKill(_)
+{
+ exists = false;
+}
+
+First, we override
the kill()
function - normally, by calling .kill()
on an object, it will set both the alive
and exists
properties to false
. In this case, we want to set alive
to false
, but we don't want to set exists
to false
just yet (objects which exists == false
don't get drawn or updated). Instead, we initialize a FlxTween
.
FlxTween
is a powerful tool that lets you animate an object's properties. For our coins, we want to make it fade out while also rising up.
We set the duration to 0.33
seconds, and we are using the circOut
easing style, to make the tween look a little nicer. Also we want to call finishKill()
when the tween has completed, which just sets the coin's exists
property to false
, effectively removing it from the screen. By default, the tween type is set to ONESHOT
so it is only executed once (instead of looping). You can change this by specifying a different type
in the tween options, but in our case the default behavior is just what we need.
The type of the FlxTween
complete callback is FlxTween->Void
(receives a single FlxTween
argument and returns nothing). In this case, we named it _
to indicate that we don't care about it, which is a common Haxe idiom (other than that, there's nothing special about it - to the compiler it's just an argument named _
).
Try out the game now, and you'll notice the difference when you pick up coins! We'll do some more of this later on when we start adding 'juice' to our game.
+ +In the next part, we'll talk about enemies!
+ + +In this section, you're going to learn a few simple tricks to add some polish and juice to your game. Some of those have already been added here and there, but we'll go through a few places now to talk about them.
+First up, there's a simple effect we can do with our camera to have it fade in or out. This works nice for transitions between states or screens.
+We can add it to each of our states' create()
function like so (right before super.create()
):
FlxG.camera.fade(FlxColor.BLACK, 0.33, true);
+
+Then, if you want to fade out (when switching states), you can do something like this:
+FlxG.camera.fade(FlxColor.BLACK, 0.33, false, function()
+{
+ FlxG.switchState(new PlayState());
+});
+
+Try adding this logic to all of your states and then see how much of an improvement it gives the flow of your game.
+Next, let's give the player a little feedback whenever they get hurt in combat. We're going to make the screen shake briefly. So, go into the enemyAttack()
function in CombatHUD
and, right after we call FlxG.camera.flash
(another neat little effect you can use in other projects), add:
FlxG.camera.shake(0.01, 0.2);
+
+So, each time the player gets hurt in combat, the screen will flash white, and will shake a little bit for 0.2
seconds. Try it out!
I know we've used tweens a few times already, but lets add one to show the enemy getting hurt in combat. We're simply going to make a tween that moves the enemy a few pixels to the right, then triggers a second tween to move the enemy back - each one taking 0.1
seconds to complete. So, in the makeChoice()
function of CombatHUD
, right before we play the hurt sound for the enemy, add:
FlxTween.tween(enemySprite, {x: enemySprite.x + 4}, 0.1, {
+ onComplete: function(_)
+ {
+ FlxTween.tween(enemySprite, {x: enemySprite.x - 4}, 0.1);
+ }
+});
+
+Check out how that looks. Tweens are a very simple and powerful tool to make your game feel more active when used properly.
+Next, let's add a background effect to our CombatHUD
to help bring our the combat screen out from the map a little bit. We're going to copy what's on the camera's buffer, desaturate it, and then apply a FlxWaveEffect
to give it a wavy effect. HaxeFlixel has several effects like this that can be used for a number of effects (FlxGlitchEffect
, FlxRainbowEffect
...).
Open up the CombatHUD
class and add this variable:
var screen:FlxSprite;
+
+In the constructor, we'll initialize these two variables (add this before we create our background
):
screen = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.TRANSPARENT);
+ var waveEffect = new FlxWaveEffect(FlxWaveMode.ALL, 4, -1, 4);
+ var waveSprite = new FlxEffectSprite(screen, [waveEffect]);
+ add(waveSprite);
+
+ First, we make our screen
, make it the size of the window, and just leave it empty for now. Then we create a FlxEffectSprite
, tell it to target our screen
, and set its properties. It only uses a single effect, our waveEffect
instance (it's possible to chain multiple effects using FlxEffectSprite
).
Next, in initCombat
we want to make our screen
take a copy of whatever is on the camera's buffer and apply it to itself, and then desaturate the image. Our effect sprite will always copy whatever is on our screen
every update automatically.
screen.drawFrame();
+ var screenPixels = screen.framePixels;
+
+ if (FlxG.renderBlit)
+ screenPixels.copyPixels(FlxG.camera.buffer, FlxG.camera.buffer.rect, new Point());
+ else
+ screenPixels.draw(FlxG.camera.canvas, new Matrix(1, 0, 0, 1, 0, 0));
+
+ var rc:Float = 1 / 3;
+ var gc:Float = 1 / 2;
+ var bc:Float = 1 / 6;
+ screenPixels.applyFilter(screenPixels, screenPixels.rect, new Point(), new ColorMatrixFilter([rc, gc, bc, 0, 0, rc, gc, bc, 0, 0, rc, gc, bc, 0, 0, 0, 0, 0, 1, 0]));
+
+That's all there is to it! Our effect sprite will fade in and out with the CombatHUD
already. Try our the effect to see how it looks!
You might have noticed, while testing the game, that the mouse cursor can get in the way - especially since it's not needed outside of the menu states. We can remedy this pretty easily.
+In the PlayState
's create()
, add:
#if FLX_MOUSE
+FlxG.mouse.visible = false;
+#end
+
+And be sure to do the reverse (= true
) in GameOverState
's create()
No more pesky mouse cursor when we don't need it!
There are plenty of other tweaks and tricks you can add to your games. Try playing around with the different addons special effects classes in HaxeFlixel and see what else you can come up with. Take a look at the demos for more examples.
+ + +Developing games in general is made easier by making the most of the resources in communities both online and offline.
+We are here collecting resources to help you learn and make games. Code snippets, tutorials and general information for game development is all wanted, so please make a pull request to these pages and make these resources even better!
+ + +The first things we need to do is install all of the components we need to work with HaxeFlixel. For that, please follow our general Getting Started guide. This will get you:
+In this tutorial we will be using Visual Studio Code (VSCode) as our editor. See here for setup and general usage instructions.
+Assuming everything went smoothly, you should be all set - now we can actually get to the fun part and start coding!
+ + +Our game is really coming together now, but it's still missing something… there's no sound yet!
+For this game, we're going to keep it simple (as usual). We're going to have a single, continuously looping track that plays while our game is running. We will also have several, simple sound effects that play for different actions. Playing sounds and music in HaxeFlixel is pretty easy, so this will go quickly!
+First, you'll need to make your music and sounds. Patrick Crecelius from Fat Bard has provided some music for this tutorial - feel free to use it for this tutorial, or make your own.
+We've also created some sound effects using Bfxr, which you can use if you like, or, make your own!
+If you've been following along since the UI and Combat section, you already downloaded a few of these, but be sure you have them all.
+coin.wav - new! to be used when the player picks up a coin
+combat.wav - to be used when combat starts
+fled.wav - will play when the player successfully flees from combat
+hurt.wav - will play whenever either the player or the enemy hits with an attack
+lose.wav - will play when the player dies in combat
+miss.wav - will play whenever either the player or the enemy misses with an attack
+select.wav - used by buttons and when the player makes a selection
+step.wav - new! used by the player and the enemies for 'footstep' sounds
+win.wav - used when the player wins in combat
+Once you have your music, place it in assets/music
, and your sound files should go in assets/sounds
.
Now let's change our code to use these sounds:
+First, open up MenuState.hx
. Since we want our music to start as soon as the game starts, and loop continuously no matter what happens, we're going to add this to create()
.
if (FlxG.sound.music == null) // don't restart the music if it's already playing
+ {
+ FlxG.sound.playMusic(AssetPaths.HaxeFlixel_Tutorial_Game__ogg, 1, true);
+ }
+
+We're also checking if the music is already playing, since we don't want to restart it unnecessarily in that case.
+If you try your game out right now, it should play music!
+Next, we want to make our buttons all make a sound when they get clicked. This is simple, we just tell the button's onUp
to load our sound. In the MenuState
's create()
, after you initialize the play-button, add this:
playButton.onUp.sound = FlxG.sound.load(AssetPaths.select__wav);
+
+Now, you can do the same for the options button (changing playButton
to optionsButton
).
For each of the other buttons in our game - four of them in OptionsState
, and one in GameOverState
- the code already exists, but as a learning exercise, you can go through those files and see what was done.
Next, let's give our player some footsteps. We don't want to create and destroy a new sound object every time we want to play the same sound, so we will create a FlxSound
object to be used over and over. At the top of the Player
class, add:
var stepSound:FlxSound;
+
+Then, we need to load the footstep sound somewhere in the constructor:
+ stepSound = FlxG.sound.load(AssetPaths.step__wav);
+
+Now go to our updateMovement()
function, and, after we check if the player is moving (if ((velocity.x != 0 || velocity.y != 0) && touching == NONE)
), add:
stepSound.play();
+
+ A neat little property of FlxSound
objects is that if you ever tell one to play, if it's already playing (and you haven't set the forceRestart
flag), it won't play again. This means we can easily call play on our sound every frame, and it will sound as if the sound is just being looped - for as long as the player is moving, but will finish if the player has stopped moving, and not start up again while they are stationary.
Now, let's give enemies their own footsteps, too. The difference is, instead of just always playing the step sound at full volume, we're going to change the volume based on the proximity of the enemy to the player. This will be easier than it sounds. First, add our sound variable to the top of Enemy.hx:
+ var stepSound:FlxSound;
+
+And then, similarly to how we setup the Player
class, add this to our constructor:
stepSound = FlxG.sound.load(AssetPaths.step__wav, 0.4);
+ stepSound.proximity(x, y, FlxG.camera.target, FlxG.width * 0.6);
+
+ You'll notice that we are setting the volume to 0.4
(40%) this is because there will be plenty of enemies on the map, and there footsteps can get kind of annoying and loud (besides, they're probably walking around the dungeon barefoot, right?).
We then setup our proximity for our sounds, setting it's position to the x
and y
position of this enemy, and telling it to target the FlxG.camera.target
object (which happens to be our player!). Finally, we say that the radius of our footstep sound is a little bit more than half of the screen's width - so we should be able to hear enemies that are just off the screen, and all the enemies' footsteps will sound louder/softer based on their distance from the camera target.
Next, similarly to where we added the player's step sounds, we're going to have the enemy play sounds, when it's playing it's walk animation. For these sounds, however, we will give the sound a position in the world.
+stepSound.setPosition(x + frameWidth / 2, y + height);
+stepSound.play();
+
+Next, let's head over to PlayState
. We really only need one sound to be in the PlayState
itself, and that's the one for picking up a coin. While you could put this into the Coin
class, because there could be a lot of coins loaded at once, and because we really can't pick up more than one coin at a time (so the sounds don't need to overlap), putting a single coin sound effect in our PlayState
saves us a bit of overhead.
So, just like our other sounds, initialize the variable:
+var coinSound:FlxSound;
+
+Load the sound in create()
:
coinSound = FlxG.sound.load(AssetPaths.coin__wav);
+
+And in playerTouchCoin()
, inside the if
-statement, add:
coinSound.play(true);
+
+This time we will use forceRestart
so that if the player happens to pickup several coins close to each other the sound will keep up with them.
All of the rest of our sounds, because they deal with combat, will be in our CombatHUD
class.
Assuming you downloaded the CombatHUD
file, the sounds should already be there, but as a learning exercise, it's a good idea to go through and check. This will help you better understand sounds for when you're working on your next game.
To initialize them:
+var fledSound:FlxSound;
+var hurtSound:FlxSound;
+var loseSound:FlxSound;
+var missSound:FlxSound;
+var selectSound:FlxSound;
+var winSound:FlxSound;
+var combatSound:FlxSound;
+
+To load them:
+fledSound = FlxG.sound.load(AssetPaths.fled__wav);
+hurtSound = FlxG.sound.load(AssetPaths.hurt__wav);
+loseSound = FlxG.sound.load(AssetPaths.lose__wav);
+missSound = FlxG.sound.load(AssetPaths.miss__wav);
+selectSound = FlxG.sound.load(AssetPaths.select__wav);
+winSound = FlxG.sound.load(AssetPaths.win__wav);
+combatSound = FlxG.sound.load(AssetPaths.combat__wav);
+
+You can probably figure out where they all go, but I'll go through them anyway.
+In initCombat()
:
combatSound.play();
+
+In finishFadeIn()
:
selectSound.play();
+
+In update()
, inside each of our three if statements related to button presses (if (_fire), else if (up), else if (down)
):
selectSound.play();
+
+In makeChoice()
, in our logic for a 'hit' (after _damages[1].text = "1";
):
hurtSound.play();
+
+and in our miss logic:
+missSound.play();
+
+Further down, if the player escapes (after outcome = ESCAPE
):
fledSound.play();
+
+In enemyAttack()
, if the enemy hits:
hurtSound.play();
+
+and if they miss:
+missSound.play();
+
+Finally, in doneDamageOut()
, after outcome = DEFEAT
:
loseSound.play();
+
+and after outcome = VICTORY
:
winSound.play();
+
+And that's it for sound! Play your game now, and you should hear all of the effects we've added (make sure your volume is up high enough, too!) It's starting to look like a real game! Next time, we'll get it working on multiple platforms!
+ + +So we have a player sprite that moves around the screen. Great! But… we don't want it to just look like a block… so let's add some graphics!
+First, you need to have the sprite's graphic. Using your image editor of choice, you can draw pretty much anything you want, and save it as a transparent .png
. For our player sprite, we're going to have it be made up of 16x16 pixel frames, with two different frames for each of the 4-possible facing directions (left and right are used by the same 2 frames, since we will be flipping them later).
You can draw it yourself, if you want, or use this image, created by my friend Vicky Hedgecock for this tutorial:
+ +Save your file in assets/images
.
Now we need to actually load the player's graphic into the sprite. So, bring up your Player
class again.
Remove the makeGraphic()
call from your constructor, and replace it with:
loadGraphic(AssetPaths.player__png, true, 16, 16);
+
+ This tells your sprite to use the player.png
graphic, that it's animated, and that each frame is 16x16 pixels. AssetPaths
is a class generated by a neat Haxe macro which builds its variables from the contents of your Project.xml
's assets tag. Macros are a little complicated, but worth taking a look into at some point. In this case, we use AssetPaths
as an easy way to reference our assets in code. Note that we could also just use a raw string path like "assets/images/player.png"
.
Next, we want to allow the sprite to be flipped based on its facing
value. This makes it so we only need sprites for one direction (left), and not two (left and right).
Add the following:
+ setFacingFlip(LEFT, false, false);
+ setFacingFlip(RIGHT, true, false);
+
+All we're doing here is saying that we don't want to flip anything when facing left (because our sprite already faces left), and to flip horizontally when facing right. We could do the same for up and down if we wanted.
+Then, we want to make the hitbox match the graphics. With this more top down perspective, it's good practice for the hitbox to match the feet, so let's set ours to the bottom middle of the sprite:
+ setSize(8, 8);
+ offset.set(4, 8);
+
+Now, we need to define our animations. In our case, we want a unique animation for each direction, except the right-facing one will be a mirrored version of the left-facing animation. We also want a "idle" and "walk" animations, depending on whether the player is moving. So, add:
+ animation.add("d_idle", [0]);
+ animation.add("lr_idle", [3]);
+ animation.add("u_idle", [6]);
+ animation.add("d_walk", [0, 1, 0, 2], 6);
+ animation.add("lr_walk", [3, 4, 3, 5], 6);
+ animation.add("u_walk", [6, 7, 6, 8], 6);
+
+ We're finished with the constructor changes, the final step is to change our updateMovement()
function to tell the player sprite which way to face. So, modify our section which deals with setting the player's angle
so that it will also set its facing
, accordingly:
if (up || down || left || right)
+ {
+ var newAngle:Float = 0;
+ if (up)
+ {
+ newAngle = -90;
+ if (left)
+ newAngle -= 45;
+ else if (right)
+ newAngle += 45;
+ facing = UP;
+ }
+ else if (down)
+ {
+ newAngle = 90;
+ if (left)
+ newAngle += 45;
+ else if (right)
+ newAngle -= 45;
+ facing = DOWN;
+ }
+ else if (left)
+ {
+ newAngle = 180;
+ facing = LEFT;
+ }
+ else if (right)
+ {
+ newAngle = 0;
+ facing = RIGHT;
+ }
+
+ // determine our velocity based on angle and speed
+ velocity.setPolarDegrees(SPEED, newAngle);
+ }
+
+ Now we can use this facing
value to determine which animation to use, but we also need to know whether the player is currently moving or not. Using these values we will determine with animation to use:
var action = "idle";
+ // check if the player is moving, and not walking into walls
+ if ((velocity.x != 0 || velocity.y != 0) && touching == NONE)
+ {
+ action = "walk";
+ }
+
+ switch (facing)
+ {
+ case LEFT, RIGHT:
+ animation.play("lr_" + action);
+ case UP:
+ animation.play("u_" + action);
+ case DOWN:
+ animation.play("d_" + action);
+ case _:
+ }
+
+Every time this function gets called, it will check to see which of the directions the player is pressing, and, based on those, which way the sprite should be facing, and which animation should be playing.
+ Note: Calling animation.play
with an animation name matching the current animation will not restart the animation.
Save your changes and run your project, and you should see your player sprite animate while being moved, and facing the right direction!
++
Next, we'll talk about maps and collision!
+ + +First, let me talk a bit about why you should use HaxeFlixel.
+HaxeFlixel gets its roots from the AS3 Flixel Framework created by +Adam ‘Atomic’ Saltsman. If you’ve played Canabalt, +you’ve seen the early version of this framework in action. HaxeFlixel takes the idea and the general structure of +Flixel, separates it from Flash, and combines it with the power and freedom of Haxe - which is a +multi-platform language, so that you can write games that can be easily published to all sorts of platforms +(Windows, Linux, Mac, HTML5, iOS, Android, and more).
+HaxeFlixel comes with a ton of built-in features - classes, utilities, and functions - that handle a lot +of common or complex things that you might need to do. And, because it’s open-source, if you find something that it +can’t do, it’s really easy to change the code - and if your changes would benefit others, +add them to the library.
+It is a library that is easy to use, but offers the freedom and power to do anything you can think of without +limitations caused by a rigid interface.
+You can find out more about HaxeFlixel here.
+ + +Unexpected behaviour one one may find with sources that are hard to determine.
+By default the library performs calculations with a fixed timestep, to change this set FlxG.fixedTimestep = false
If you are unfamiliar with timesteps, here is a quick explanation.
+A fixed timestep results in FlxG.elapsed
always returning the same value in each update() iteration. The result is that the game runs "slower" if the computer can't keep up with the update speed. So for example, if the game is supposed to run at 60 FPS and it's running at 30 FPS, the user will perceive it to run at half the expected speed.
A variable timestep results in FlxG.elapsed
returning a value according to the time that passed since the last update() call, and the game running at perceivably the expected speed for the user. However if the framerate is too low and the coder hasn't been careful (or studied Numerical Analysis), it could result in unpredicted behaviour and a game that doesn't run slow but is simply unplayable or broken.
A possible cause for this is related to input handling. To fix, you may want to disable any inputs you don't use in your target platforms at compile time. +The library offers some conditional compilation flags for this:
+These can be passed as parameters preceded by -D
if compiling via command line (for example -D FLX_NO_MOUSE
), or passed by adding them to your Project.XML file if you have one. For example, someone who is publishing to multiple platforms could have:
<haxedef name="FLX_NO_MOUSE" if="mobile" />
+<haxedef name="FLX_NO_KEYBOARD" />
+<haxedef name="FLX_NO_TOUCH" if="desktop"/>
+<haxedef name="FLX_NO_GAMEPAD" if="web"/>
+
+If you created your Project.XML from a template, these options are most likely already in the file but commented out.
+Assigning a new instance of any of the classes that inherit from BaseScaleMode to FlxG.scalemode
will set this to the desired scaling. However these settings may override the zoom value set when instancing a FlxGame
.
Welcome to HaxeFlixel! In this tutorial, you will learn how to create a complete game in HaxeFlixel from start to finish, and then some. Each step is broken into smaller chunks to help you get through even the most complicated and daunting steps.
+This tutorial is geared towards building for HTML5, Windows, and Android. We will work primarily with HTML5 most of the time.
+We are going to be building a small dungeon crawler. You can see the finished product here:
+
+
If you have any questions or get stuck during this tutorial, you can get help here. Also, the finished game is part of flixel-demos, so you can always compare to the source code of the finished game if you're unsure about something.
+So let's go ahead and get started!
+ + +Now we want to show the player what's going on. So we need to have some kind of HUD on the screen to tell them what their current/max health is, and how many coins they have. For the health icon you can use this image , or make your own. Make sure you save this in assets/images
.
We'll start by making a new HUD
class which will hold all our HUD elements:
package;
+
+ import flixel.FlxG;
+ import flixel.FlxSprite;
+ import flixel.group.FlxGroup.FlxTypedGroup;
+ import flixel.text.FlxText;
+ import flixel.util.FlxColor;
+
+ using flixel.util.FlxSpriteUtil;
+
+ class HUD extends FlxTypedGroup<FlxSprite>
+ {
+ var background:FlxSprite;
+ var healthCounter:FlxText;
+ var moneyCounter:FlxText;
+ var healthIcon:FlxSprite;
+ var moneyIcon:FlxSprite;
+
+ public function new()
+ {
+ super();
+ background = new FlxSprite().makeGraphic(FlxG.width, 20, FlxColor.BLACK);
+ background.drawRect(0, 19, FlxG.width, 1, FlxColor.WHITE);
+ healthCounter = new FlxText(16, 2, 0, "3 / 3", 8);
+ healthCounter.setBorderStyle(SHADOW, FlxColor.GRAY, 1, 1);
+ moneyCounter = new FlxText(0, 2, 0, "0", 8);
+ moneyCounter.setBorderStyle(SHADOW, FlxColor.GRAY, 1, 1);
+ healthIcon = new FlxSprite(4, healthCounter.y + (healthCounter.height/2) - 4, AssetPaths.health__png);
+ moneyIcon = new FlxSprite(FlxG.width - 12, moneyCounter.y + (moneyCounter.height/2) - 4, AssetPaths.coin__png);
+ moneyCounter.alignment = RIGHT;
+ moneyCounter.x = moneyIcon.x - moneyCounter.width - 4;
+ add(background);
+ add(healthIcon);
+ add(moneyIcon);
+ add(healthCounter);
+ add(moneyCounter);
+ forEach(function(sprite) sprite.scrollFactor.set(0, 0));
+ }
+
+ public function updateHUD(health:Int, money:Int)
+ {
+ healthCounter.text = health + " / 3";
+ moneyCounter.text = Std.string(money);
+ moneyCounter.x = moneyIcon.x - moneyCounter.width - 4;
+ }
+ }
+
+ This class extends FlxTypedGroup<FlxSprite>
so that it can hold all of our FlxSprite
objects. It is composed of 5 different items: a background (black, with a 1-pixel thick white line along the bottom), 2 FlxText
objects: 1 for health, and 1 for money, and two FlxSprite
objects, for the icons to go next to the FlxText
objects. At the end of our constructor, we have a forEach()
call - we use this to iterate through each of the items in this group, and it just sets their scrollFactor.x
and scrollFactor.y
to 0, meaning, even if the camera scrolls, all of these items will stay at the same position relative to the screen.
Finally, we have a function that we can call from anywhere to tell the HUD
what it should display.
Now let's get it to work and have it update whenever we pick up a coin. In your PlayState
, add this to the top of the class:
var hud:HUD;
+ var money:Int = 0;
+ var health:Int = 3;
+
+In create()
, before super.create()
, add:
hud = new HUD();
+ add(hud);
+
+Finally, in the playerTouchCoin()
function we added earlier, somewhere inside the if
-statement, add:
money++;
+ hud.updateHUD(health, money);
+
+Go ahead and test out your game, and the HUD should update each time you pick up a coin!
+ +If we had a way to 'hurt' the player, we could also update the health on the HUD… but in order to do that, we need to figure out how we're going to do combat!
+Let's begin by establishing what we want our combat system to achieve. First, we're not going to be making the next Final Fantasy game here, this is just a basic demonstration to show how a few different elements can work. So, I think all we want to do is have a simple interface that appears when the player touches an enemy that shows the player's health, and the enemy's health (in a health bar, for obfuscation), and gives the player 2 options: FIGHT
or FLEE
.
If they choose to fight, we'll roll some random chance checks to see if the player hits the enemy, and if the enemy hits the player - a hit will do 1 damage. Once the enemy dies, we'll continue on. If they choose to flee, we'll do a check to see if they do flee or not - if they do, the interface closes and the enemy will be stunned for a few seconds so the player can move away. If they fail to flee, the enemy will get a free hit against the player. We'll also show the damage and misses on the interface.
+This all seems simple enough, but it's actually going to require several components working together to make it work. It's the most complicated piece of our game so far.
+The first component will be our CombatHUD
class. This is a pretty big class - it's going to do most of the heavy lifting with our combat logic. You can see the complete class here:
Take some time to read through it to see how it works, then add it to your project.
+ We already have most of the assets used by the CombatHUD
, but there is one image file we still need - an arrow the player can use to select a choice. Download it from this link (or make your own), name it pointer.png
and add it to the assets/images
folder.
The CombatHUD
also uses something we haven't discussed yet: sounds. We'll dig in to this more in the Sound and Music section. For now, just download these files and place them in the assets/sounds
folder. This will ensure the code compiles.
Now, you will need to add a small function to our Enemy
class:
public function changeType(type:EnemyType)
+ {
+ if (this.type != type)
+ {
+ this.type = type;
+ var graphic = if (type == BOSS) AssetPaths.boss__png else AssetPaths.enemy__png;
+ loadGraphic(graphic, true, 16, 16);
+ }
+ }
+
+Next, we need to get our CombatHUD
into our PlayState
. Add this to the top of the PlayState
class:
var inCombat:Bool = false;
+ var combatHud:CombatHUD;
+
+Move down to create()
, and, after we add the HUD, and before we call super.create()
, add:
combatHud = new CombatHUD();
+ add(combatHud);
+
+Go down to our update()
, and change it so that we're ONLY checking for collisions and overlaps when we're not in combat. Everything after the super.update()
should look like this:
if (inCombat)
+ {
+ if (!combatHud.visible)
+ {
+ health = combatHud.playerHealth;
+ hud.updateHUD(health, money);
+ if (combatHud.outcome == VICTORY)
+ {
+ combatHud.enemy.kill();
+ }
+ else
+ {
+ combatHud.enemy.flicker();
+ }
+ inCombat = false;
+ player.active = true;
+ enemies.active = true;
+ }
+ }
+ else
+ {
+ FlxG.collide(player, walls);
+ FlxG.overlap(player, coins, playerTouchCoin);
+ FlxG.collide(enemies, walls);
+ enemies.forEachAlive(checkEnemyVision);
+ FlxG.overlap(player, enemies, playerTouchEnemy);
+ }
+
+So, we're adding a check to see if the player touches an enemy. If they do, we'll call a callback to see if we should start combat or not.
+ If we're in combat, we're simply going to keep checking to see if the combat HUD is still visible - once it becomes invisible, we know that combat has finished, and we can determine the outcome. If the outcome is VICTORY
(one of our four enum values), we will kill the enemy, but if the player fled the battle, we will make the enemy flicker, to show that the player is safe from fighting it again for a short amount of time.
You may have noticed that our Enemy
class does not have a flicker()
function. That's because we're going to use one found in the FlxSpriteUtil
class. Haxe has a nice feature to help us do so. Add this line at the top of the PlayState
file, just after your imports:
using flixel.util.FlxSpriteUtil;
+
+ This will allow us to use the APIs in the FlxSpriteUtil
class, such as flicker()
, which can be used on any FlxObject
. For more on how this works, take a look at the Haxe documentation.
Next, let's add the functions to handle the player touching an enemy:
+ function playerTouchEnemy(player:Player, enemy:Enemy)
+ {
+ if (player.alive && player.exists && enemy.alive && enemy.exists && !enemy.isFlickering())
+ {
+ startCombat(enemy);
+ }
+ }
+
+ function startCombat(enemy:Enemy)
+ {
+ inCombat = true;
+ player.active = false;
+ enemies.active = false;
+ combatHud.initCombat(health, enemy);
+ }
+
+All we're doing here is verify that both the player and the enemy are alive and exist, as well as that the enemy is not flickering (flickering enemies are those we've just fled from). If so, we start combat.
+ The startCombat()
function simply sets our inCombat
flag (so we know not to do collisions), and sets the player and all the enemies to inactive, so they no longer update.
Finally, we call initCombat()
in our CombatHUD
, which initializes it and makes it start working.
Finally, we want enemies that are flickering to not move - they should act kind of stunned for a second after the enemy flees.
+ In the Enemy
class, under update()
, add:
if (this.isFlickering())
+ return;
+
+At the very top, before doing anything else in that function.
+ Note that isFlickering()
comes from FlxSpriteUtil
. So, just like before, you will also need to add the using
line at the top of the Enemy
class file:
using flixel.util.FlxSpriteUtil;
+
+And that should do it! Test out your game and make sure that it works!
+ +Next, we'll cover winning and losing and setting up all our different states.
+ + +The minimum Haxe version for this release is 3.1.0.
+On flash, gamepad support has been added. For your project to work, you have to modify your Project.xml
so that either:
<set name="SWF_VERSION" value="11.8" />
is used (min. flash player version 11.8) or <haxedef name="FLX_NO_GAMEPAD" if="flash" />
is used.When compiling to HTML5, make sure to remove <haxelib name="openfl" />
from your Project.xml
. This is already being handled in flixel's own include.xml
.
HaxeFlixel 3.2.x | +HaxeFlixel 3.3.0 | +
---|---|
FlxTween.multiVar() | +FlxTween.tween() | +
FlxTween.singleVar() | +FlxTween.tween() | +
FlxTween.fader(0, 5); | +FlxTween.tween(FlxG.sound, {volume: 0}, 5); | +
FlxSound.survive | +FlxSound.persist | +
MouseEventManager.addSprite() | +MouseEventManager.add() | +
FlxObject.forceComplexRender = true; | +FlxObject.pixelPerfectRender = false; | +
FlxText.width | +FlxText.fieldWidth | +
FlxSprite.setOriginToCenter() | +FlxSprite.centerOrigin() | +
FlxG.safeDestroy() | +FlxDestroyUtil.destroy() | +
FlxTilemap.scaleX | +FlxTilemap.scale.x | +
FlxTilemap.scaleY | +FlxTilemap.scale.y | +
sprite.animation.addByIndicies() | +sprite.animation.addByIndices() | +
sprite.animation.addByStringIndicies() | +sprite.animation.addByStringIndices() | +
FlxTimer.userData | +removed | +
FlxTween.userData | +removed | +
FlxG.sound.add() | +FlxG.sound.cache() | +
There has also been a slight optimization for simple FlxSprites in FLX_RENDER_BLIT
mode (flash and HTML5). This might require an additional dirty = true;
for the change to show up if you manipulate the sprite's BitmapData
directly.
Flipping sprite graphics now works differently - the flipped
variable has been removed, as well as the Reverse
parameter of loadGraphic()
and loadGraphicFromTexture()
. You can now directly manipulate the new flipX
and flipY
variables (flipping vertically is now possible).
If you want to continue to use facing
to flip the graphic (e.g. in a platformer), you can use the following logic:
sprite.setFacingFlip(RIGHT, false, false);
+sprite.setFacingFlip(LEFT, true, false);
+
+FlxPath
and FlxTimer
were being pooled internally, which could lead to buggy behaviour in certain use cases. Due to that, pooling has been removed from these classes.
HaxeFlixel 3.2.x | +HaxeFlixel 3.3.0 | +
---|---|
start() | +new FlxTimer() / FlxPath() | +
run() | +start() | +
Also, the following API changes have been made for consistency:
+HaxeFlixel 3.2.x | +HaxeFlixel 3.3.0 | +
---|---|
paused | +active | +
abort() | +cancel() | +
HaxeFlixel 3.1.0 | +HaxeFlixel 3.2.0 | +
---|---|
FlxTween.multiVar(object, vars, duration, { delay: 1}); | +FlxTween.multiVar(object, vars, duration, { startDelay: 1}); | +
FlxG.camera.followAdjust(4, 5); | +FlxG.cameras.followLead.set(4, 5); | +
FlxTilemap.arrayToCSV() | +FlxStringUtil.arrayToCSV() | +
FlxTilemap.bitmapToCSV() | +FlxStringUtil.bitmapToCSV() | +
FlxTilemap.imageToCSV() | +FlxStringUtil.imageToCSV() | +
FlxMath.computeVelocity() | +FlxVelocity.computeVelocity() | +
FlxState.setSubState() | +FlxState.openSubState() | +
HaxeFlixel 3.1 is a continuation of our efforts of making the API cleaner and more intuitive, keeping the amount of bugs as low as possible and adding new features. It is one of the biggest releases so far.
+This page is a summary of all breaking changes - for a more in-depth list of changes, please refer to the changelog.
+FlxTypedButton
/ FlxButton
refactorFlxTypedButton
has been completely refactored, which includes the following breaking API changes:
A new FlxButtonEvent
class was added for the onDown, onUp, onOver and onOut events. Instead of the setCallback()
-functions, you now set callbacks like this:
button.onDown.callback = someFunction;
+
This class also contains a sound
property:
button.onDown.sound = FlxG.sound.load("pathToASound");
+
You might say: "What happened to custom callback arguments? The callback
has the type Void->Void
!"
+ While that's true, you can still use callback arguments by leveraging function binding:
button.onDown.callback = someFunction.bind(1);
+
In that example, someFunction
is of type Int->Void
and will always be called with 1.
labelOffset:FlxPoint
is now an array (labelOffsets:Array<FlxPoint>
) which uses the button status constants as indices for more control over the button position.
FlxVirtualPad
more usable.In 3.0.0, FlxG.keyboard has been introduced. However, we realized that this does not make for an intuitive API - you can't tell the difference between the two from their name alone. In fact, even if you have been using the two for a while, it still seems confusing.
+This is why me merged the two classes again. This required removing the following functions:
+FlxG.keyboard.pressed()
FlxG.keyboard.justPressed()
FlxG.keyboard.justReleased()
You should use the following functions instead:
+FlxG.keys.anyPressed()
FlxG.keys.anyJustPressed()
FlxG.keys.anyJustReleased()
Please note that those functions take an Array<String>
instead of a variable amount of String
s. So the following...
if (FlxG.keyboard.pressed("LEFT", "RIGHT")) {}
+
+...becomes:
+if (FlxG.keys.anyPressed(["LEFT", "RIGHT"])) {}
+
+The following breaking changes were made during the refactor of FlxMouse
:
HaxeFlixel 3.0.x | +HaxeFlixel 3.1.0 | +
---|---|
FlxG.mouse.show(); | +FlxG.mouse.visible = true; | +
+ | FlxG.mouse.load(); | +
FlxG.mouse.hide(); | +FlxG.mouse.visible = false; | +
FlxState.useMouse | +removed | +
Also, the mouse cursor is now visible by default on non-mobile devices.
+The middle and right click event listeners are now set up by default, which means FLX_MOUSE_ADVANCED
has turned into FLX_NO_MOUSE_ADVANCED
.
FlxRandom
To put it bluntly... FlxRandom
was a bit of a mess in 3.0. Some of the functions were deterministic, others weren't, which as a result made it very difficult to create deterministic games suitable for the recording system.
In 3.1.0, FlxRandom
has been refactored to be completely deterministic. A new algorithm for pseudo-random-number-generation was implemented, which makes old replays incompatible / they will have unpredictable results.
Additionally, the following functions have been added to FlxRandom
:
weightedPick()
weightedGetObject()
colorExt()
A noteworthy amount of fields inside of FlxSprite
have been renamed to increase the consistency with other parts of the API:
HaxeFlixel 3.0.x | +HaxeFlixel 3.1.0 | +
---|---|
loadfromSprite() | +loadGraphicFromSprite() | +
setGraphicDimensions() | +setGraphicSize() | +
bakedRotation | +bakedRotationAngle | +
pixelsOverlapPoint() | +overlapsPoint() | +
loadImageFromTexture() | +loadGraphicFromTexture() | +
loadRotatedImageFromTexture() | +loadRotatedGraphicFromTexture() | +
setColorTransformation() | +setColorTransform() | +
HaxeFlixel 3.1.0 introduces scale modes to simplify targeting multiple resolutions. FlxG.scaleMode
can be an instance of the following classes:
RatioScaleMode
(default!)FillScaleMode
FixedScaleMode
RelativeScaleMode
StageSizeScaleMode
This change made FlxG.autoResize
obsolete and it has thus been removed.
You can also write a custom scale mode that extends BaseScaleMode
.
Be sure to check out the ScaleModes demo.
+The way FlxTypedGroup.sort()
has been changed for a significant performance improvement. If you want to sort by y, you now have to use the following syntax:
group.sort(FlxSort.byY, FlxSort.ASCENDING);
+
+Instead of:
+group.sort("y", FlxSort.ASCENDING);
+// or
+group.sort();
+
+If you want to sort by anything other than y
, you'll have to write a custom sorting function, as can be seen in this example:
function sortByAlpha(Order:Int, Sprite1:FlxSprite, Sprite2:FlxSprite):Int
+{
+ return FlxSort.byValues(Order, Sprite1.alpha, Sprite2.alpha);
+}
+
+// usage on a FlxTypedGroup<FlxSprite>:
+group.sort(sortByAlpha);
+
+HaxeFlixel 3.0 | +HaxeFlixel 3.1 | +
---|---|
FlxTypedGroup.autoReviveMembers | +removed | +
FlxG.gameFramerate | +FlxG.updateFramerate | +
FlxG.flashFramerate | +FlxG.drawFramerate | +
FlxG.gamepads.get() | +FlxG.gamepads.getByID() | +
FlxG.debugger.visualDebug | +FlxG.debugger.drawDebug | +
FlxG.paused | +removed (didn't have any functionality) | +
FlxArrayUtil.intToString() | +FlxStringUtil.toIntArray() | +
FlxArrayUtil.floatToString() | +FlxStringUtil.toFloatArray() | +
FlxMisc.openURL() | +FlxG.openURL() | +
FlxMisc | +removed | +
FlxSoundUtil | +removed (use a FlxTimer instead) | +
The classes from flixel.system.input
have been moved to flixel.input
.
FlxPoint
s in FlxObject
and FlxSprite
are now read-only / (default, null)
, which means you need to use .set() on them if you were previously creating new points. The following...
sprite.velocity = new FlxPoint(100, 50);
+
+...becomes:
+sprite.velocity.set(100, 50);
+
+HaxeFlixel 3.0 is an evolution of the original Flixel API and while most of the API is very similar and quickly learnt, it requires some renames and modifications to update your code.
+We wanted to slim down up the core classes, which meant moving non-essential functionality into separate classes. We've also continued to focus on improving stability and adding features to the engine. Here's a quick overview of the biggest changes:
+mySprite.animation.add(...);
+
+mySprite.animation.play(...);
+
+mySprite.animation.frameName = "String";
+
+mySprite.animation.frameIndex = Int;
+
+//inspect the advanced api features such as
+addByNames, addByStringIndicies, addByIndicies, addByPrefix, findSpriteFrame, randomFrame ...
+
+
Added new FlxKeyShortcuts class, which replaces FlxG.keys. FlxG.keys.pressed is no longer a function, it's now an object, ex: if( FlxG.keys.pressed.ANY ) {...}
Added new FlxSpriteGroup class, which allows an FlxGroup to behave like an FlxSprite. This is a powerful new construct that will simplify building UI controls.
+FlxU is now gone, we've moved all its functionality to several utility classes that specific contain functionality, ex: FlxArrayUtil, FlxAngle, FlxMath, FlxRandom, FlxSpriteUtil, FlxVelocity, etc.
+FlxSprite Filters are now in a separate FlxSpriteFilter utility class.
+HaxeFlixel no longer has an org
package. Everything is now included as flixel.package.Class
. For most cases you can just remove org.
from your import statements.
This was a decision the core developers agreed upon, to make the package structure simpler and detach HaxeFlixel from old flash conventions.
+Frontends in HaxeFlixel 3.x are a new structure to the core of Flixel and which tackles the often criticized bloated collection of static methods in FlxG.
+Frontends are accessed in FlxG.frontend
in a similar fashion to what Flixel devs are used to. Careful thought has been given to organise them into logical shortcuts. This way the api will be easier browse, remember and maintain.
For example in HaxeFlixel 2.x to add a FlxCamera
you would use FlxG.addCamera(camera:FlxCamera);
, this addCamera
method has been moved into a camera
frontend with all the other camera
related shortcuts.
+So the code in HaxeFlixel 3.x to add a FlxCamera
is now FlxG.cameras.add(camera:FlxCamera)
.
The Flixel FrontEnds are as follows:
+More detail on the FrontEnds can be read on the FrontEnd docs page.
+HaxeFlixel has system Assets for its debugger buttons, system sounds, etc. These assets were previously stored in every project in the assets/data folder. HaxeFlixel 3.x uses the OpenFL include.xml in core HaxeFlixel to omit the need to include them in every project.
+So you do not need to have system assets anymore, everything in your project's ./assets/*
folder should only be the assets you create.
HaxeFlixel 3.x includes a powerful console and improved debugger. The new debugger system by default redirects the core trace()
command to the log. Alternatively you can use FlxG.log.add()
, FlxG.watch.add()
, FlxG.log.warn
and more.
Our command line tools have been moved to an optional repository, so the old haxelib run flixel new
command will not work.
+Install the tools from haxelib just like flixel and run setup and follow the prompts:
haxelib install flixel-tools
+
+haxelib run flixel-tools setup
+
Now you can use the commands with just flixel
, try the help command for more info.
flixel help
+
+//see the new template tool options with:
+flixel help template
+
+//Shorthand version to create a template with a custom name
+flixel tpl -n "CustomProject"
+
A collection of most of the API name changes were collected for the flixel-tools command line tool. +You can see what it replaces here.
+To run the find and replace the command is simple:
+flixel convert
+
The main changes that developers will notice are as follows:
+HaxeFlixel 2.x | +HaxeFlixel 3.x | +
---|---|
FlxG.getLibraryName() | +FlxG.libraryName | +
FlxG.setDebuggerLayout | +FlxG.debugger.setLayout | +
FlxG.log | +trace() | +
FlxG.resetDebuggerLayout | +FlxG.debugger.resetLayout | +
FlxG.visualDebug | +FlxG.debugger.visualDebug | +
FlxG.toggleKeys | +FlxG.debugger.toggleKeys | +
FlxG.DEBUGGER_STANDARD | +FlxDebugger.STANDARD | +
FlxG.DEBUGGER_MICRO | +FlxDebugger.MICRO | +
FlxG.DEBUGGER_BIG | +FlxDebugger.BIG | +
FlxG.DEBUGGER_TOP | +FlxDebugger.TOP | +
FlxG.DEBUGGER_LEFT | +FlxDebugger.LEFT | +
FlxG.DEBUGGER_RIGHT | +FlxDebugger.RIGHT | +
FlxG.random | +FlxRandom.float | +
FlxG.shuffle | +FlxArrayUtil.shuffle | +
FlxG.getRandom | +FlxArrayUtil.getRandom | +
FlxG.globalSeed | +FlxRandom.globalSeed | +
FlxG.tween | +FlxTween.multiVar | +
FlxG.resetInput | +FlxG.inputs.reset | +
FlxG.RED | +FlxColor.RED | +
FlxG.GREEN | +FlxColor.GREEN | +
FlxG.BLUE | +FlxColor.BLUE | +
FlxG.PINK | +FlxColor.PINK | +
FlxG.WHITE | +FlxColor.WHITE | +
FlxG.BLACK | +FlxColor.BLACK | +
FlxG.TRANSPARENT | +FlxColor.TRANSPARENT | +
FlxG.DEG | +FlxAngle.TO_DEG | +
FlxG.RAD | +FlxAngle.TO_RAD | +
FlxG.flashGfx | +FlxSpriteUtil.flashGfx | +
FlxG.flashGfxSprite | +FlxSpriteUtil.flashGfxSprite | +
FlxG.levels | +Reg.levels | +
FlxG.scores | +Reg.scores | +
FlxG.score | +Reg.score | +
FlxG.saves | +Reg.saves | +
FlxG.save | +Reg.save | +
FlxG.addCamera | +FlxG.cameras.add | +
FlxG.useBufferLocking | +FlxG.cameras.useBufferLocking | +
FlxG.lockCameras | +FlxG.cameras.lock | +
FlxG.renderCameras | +FlxG.cameras.render | +
FlxG.unlockCameras | +FlxG.cameras.unlock | +
FlxG.removeCamera | +FlxG.cameras.remove | +
FlxG.resetCameras | +FlxG.cameras.reset | +
FlxG.shake | +FlxG.cameras.shake | +
FlxG.bgColor | +FlxG.cameras.bgColor | +
FlxG.warn | +FlxG.log.warn | +
FlxG.error | +FlxG.log.error | +
FlxG.notice | +FlxG.log.notice | +
FlxG.advancedLog | +FlxG.log.advanced | +
FlxG.clearLog | +FlxG.log.clear | +
FlxG.watch | +FlxG.watch.add | +
FlxG.unwatch | +FlxG.watch.remove | +
FlxG.play | +FlxG.sound.play | +
FlxG.playMusic | +FlxG.sound.playMusic | +
FlxG.loadSound | +FlxG.sound.load | +
FlxG.addSound | +FlxG.sound.add | +
FlxG.stream | +FlxG.sound.stream | +
FlxG.destroySounds | +FlxG.sound.destroySounds | +
FlxG.updateSounds | +FlxG.sound.updateSounds | +
FlxG.pauseSounds | +FlxG.sound.pauseSounds | +
FlxG.resumeSounds | +FlxG.sound.resumeSounds | +
FlxG.music | +FlxG.sound.music | +
FlxG.sounds | +FlxG.sound.list | +
FlxG.mute | +FlxG.sound.muted | +
FlxG.volume | +FlxG.sound.volume | +
FlxG.volumeHandler | +FlxG.sound.volumeHandler | +
FlxG.keyVolumeUp | +FlxG.sound.keyVolumeUp | +
FlxG.keyVolumeDown | +FlxG.sound.keyVolumeDown | +
FlxG.keyMute | +FlxG.sound.keyMute | +
FlxG.addPlugin | +FlxG.plugins.add | +
FlxG.getPlugin | +FlxG.plugins.get | +
FlxG.removePlugin | +FlxG.plugins.remove | +
FlxG.removePluginType | +FlxG.plugins.removeType | +
FlxG.updatePlugins | +FlxG.plugins.update | +
FlxG.drawPlugins | +FlxG.plugins.draw | +
FlxG.plugins | +FlxG.plugins.list | +
FlxG.loadReplay | +FlxG.vcr.loadReplay | +
FlxG.reloadReplay | +FlxG.vcr.reloadReplay | +
FlxG.stopReplay | +FlxG.vcr.stopReplay | +
FlxG.recordReplay | +FlxG.vcr.startRecording | +
FlxG.stopRecording | +FlxG.vcr.stopRecording | +
FlxG.checkBitmapCache | +FlxG.bitmap.checkCache | +
FlxG.createBitmap | +FlxG.bitmap.create | +
FlxG.addBitmap | +FlxG.bitmap.add | +
FlxG.removeBitmap | +FlxG.bitmap.remove | +
FlxG.getCacheKeyFor | +FlxG.bitmap.getCacheKeyFor | +
FlxG.getUniqueBitmapKey | +FlxG.bitmap.getUniqueKey | +
FlxG.clearBitmapCache | +FlxG.bitmap.clearCache | +
FlxG.clearAssetsCache | +FlxG.bitmap.clearAssetsCache | +
Breaking changes in 4.4.0 are limited to usage with OpenFL 8. OpenFL 3.6.1 (Legacy or Next) is still fully supported. The breaking changes when upgrading to OpenFL 8.0.0 and Lime 6.3.0 are as follows:
+FlxSprite.blend
). This is because drawQuads()
(the rendering API replacing drawTiles()
in OpenFL 8), doesn't support them. In some cases, blend modes may be emulated using shaders. An example of this can be seen in our BlendModeShaders demo.flixel.effects.postprocess
API is not supported in OpenFL 8. As a replacement, a shader filter can be applied to the FlxGame
instance or a FlxCamera
as shown in the Filters demo.-Dnext
had support for per-sprite, per-camera or game-wide GLSL shaders. All of these are still fully supported, but the syntax has changed a bit.Here's a simple example of a shader found in FlxBunnyMark:
+With OpenFL 3.6.1 + Next:
+import openfl.display.Shader;
+
+class Invert extends Shader
+{
+ @fragment var fragment = '
+ void main()
+ {
+ vec4 color = texture2D(${Shader.uSampler}, ${Shader.vTexCoord});
+ gl_FragColor = vec4((1.0 - color.r) * color.a, (1.0 - color.g) * color.a, (1.0 - color.b) * color.a, color.a);
+ }';
+
+ public function new()
+ {
+ super();
+ }
+}
+
+With OpenFL 8:
+import flixel.system.FlxAssets.FlxShader;
+
+class Invert extends FlxShader
+{
+ @:glFragmentSource('
+ #pragma header
+
+ void main()
+ {
+ vec4 color = flixel_texture2D(bitmap, openfl_TextureCoordv);
+ gl_FragColor = vec4((1.0 - color.r) * color.a, (1.0 - color.g) * color.a, (1.0 - color.b) * color.a, color.a);
+ }'
+ )
+
+ public function new()
+ {
+ super();
+ }
+}
+
+To summarize the differences, shaders should...
+flixel.system.FlxAssets.FlxShader
instead of openfl.display.Shader
@:glFragmentSource()
metadata for the shader source rather than a @fragment var
#pragma header
before main()
Attributes have changed as follows:
+With OpenFL 3.6.1 + Next | +With OpenFL 8 | +
---|---|
${Shader.uSampler} |
+bitmap |
+
${Shader.vTexCoord} |
+openfl_TextureCoordv |
+
${Shader.uTextureSize} |
+openfl_TextureSize |
+
You may also have noticed that in the invert shader example, texture2D()
has been replaced with flixel_texture2D()
. The former still works, but when using flixel_texture2D()
in per-sprite shaders, the alpha
and color transforms of a FlxSprite
are already applied on the returned color, which was previously not supported. The effect of this can be seen when activating shaders as well as toggling "Simple" to "Complex" in FlxBunnyMark:
With OpenFL 3.6.1 + Next | +With OpenFL 8 | +
---|---|
+ | + |
Further shader examples can be found in these demos, which are all compatible with both OpenFL 3.6.1 + Next and OpenFL 8:
+ +FlxTween.manager
is now FlxTween.globalManager
.FlxTimer.manager
is now FlxTimer.globalManager
.FlxCamera
's scroll bounds now account for zoom
. This means that you may need to adjust calls to setScrollBounds()
, setScrollBoundsRect()
or changes to the minScrollX
/ Y
/ maxScrollX
/ Y
properties if they manually accounted for zoom.active
variable of objects in flixel.util.helpers
now defaults to true
instead of false
.This guide is intended for users upgrading projects from version 3.3.x to 4.0.0. For non-breaking changes, please refer to the changelog.
+The minimum required Haxe version for this release is 3.2.0.
+A lot of changes can be handled with a simple find-and-replace in the editor of your choice.
+elapsed
argument added to update()
The function signature of update()
changed to update(elapsed:Float)
. FlxG.elapsed
is still available, but it is recommended to use the argument value instead.
HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
override public function update():Void |
+override public function update(elapsed:Float):Void |
+
super.update(); |
+super.update(elapsed); |
+
x += 100 * FlxG.elapsed; |
+x += 100 * elapsed; |
+
flixel.math
A new flixel.math
package was added. A number of flixel.util
classes have been moved there:
HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
import flixel.util.FlxMath |
+import flixel.math.FlxMath |
+
import flixel.util.FlxPoint |
+import flixel.math.FlxPoint |
+
import flixel.util.FlxVector |
+import flixel.math.FlxVector |
+
import flixel.util.FlxRect |
+import flixel.math.FlxRect |
+
import flixel.util.FlxAngle |
+import flixel.math.FlxAngle |
+
import flixel.util.FlxVelocity |
+import flixel.math.FlxVelocity |
+
import flixel.util.FlxRandom |
+import flixel.math.FlxRandom |
+
Typed classes have been moved into the modules of the non-typed versions:
+HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
import flixel.group.FlxTypedGroup |
+import flixel.group.FlxGroup |
+
import flixel.group.FlxTypedSpriteGroup |
+import flixel.group.FlxSpriteGroup |
+
import flixel.effects.particles.FlxTypedEmitter |
+import flixel.effects.particles.FlxEmitter |
+
import flixel.ui.FlxTypedButton |
+import flixel.ui.FlxButton |
+
ActionScript 3 does not have enums, which is why a lot of these "value sets" were implemented using integer constants. For improved type-safety and to better fit the Haxe coding style, they have been converted to enums:
+As long as it's not ambiguous, Haxe allows using just the enum value name without the enum's name. In the first case, the enum also does not need to be imported. For example, this means that both of these syntaxes are valid:
+FlxG.camera.follow(LOCKON);
+FlxG.camera.follow(FlxCameraFollowStyle.LOCKON);
+
+Which of these two styles is used is mostly a matter of personal preference.
+FlxCamera
shake modes:HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
FlxCamera.SHAKE_BOTH_AXES |
+flixel.util.FlxAxes.XY |
+
FlxCamera.SHAKE_HORIZONTAL_ONLY |
+flixel.util.FlxAxes.X |
+
FlxCamera.SHAKE_VERTICAL_ONLY |
+flixel.util.FlxAxes.Y |
+
FlxCamera
follow styles:HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
FlxCamera.STYLE_LOCKON |
+FlxCameraFollowStyle.LOCKON |
+
FlxCamera.STYLE_NO_DEAD_ZONE |
+FlxCameraFollowStyle.NO_DEAD_ZONE |
+
FlxCamera.STYLE_PLATFORMER |
+FlxCameraFollowStyle.PLATFORMER |
+
FlxCamera.STYLE_SCREEN_BY_SCREEN |
+FlxCameraFollowStyle.SCREEN_BY_SCREEN |
+
FlxCamera.STYLE_TOPDOWN |
+FlxCameraFollowStyle.TOPDOWN |
+
FlxCamera.STYLE_TOPDOWN_TIGHT |
+FlxCameraFollowStyle.TOPDOWN_TIGHT |
+
FlxText
border styles:HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
FlxText.BORDER_NONE |
+FlxTextBorderStyle.NONE |
+
FlxText.BORDER_OUTLINE |
+FlxTextBorderStyle.OUTLINE |
+
FlxText.BORDER_OUTLINE_FAST |
+FlxTextBorderStyle.OUTLINE_FAST |
+
FlxText.BORDER_SHADOW |
+FlxTextBorderStyle.SHADOW |
+
FlxTilemap
auto-tiling options:HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
FlxTilemap.ALT |
+FlxTilemapAutoTiling.ALT |
+
FlxTilemap.AUTO |
+FlxTilemapAutoTiling.AUTO |
+
FlxTilemap.OFF |
+FlxTilemapAutoTiling.OFF |
+
FlxBar
fill directions:HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
FlxBar.FILL_BOTTOM_TO_TOP |
+FlxBarFillDirection.BOTTOM_TO_TOP |
+
FlxBar.FILL_HORIZONTAL_INSIDE_OUT |
+FlxBarFillDirection.HORIZONTAL_INSIDE_OUT |
+
FlxBar.FILL_HORIZONTAL_OUTSIDE_IN |
+FlxBarFillDirection.HORIZONTAL_OUTSIDE_IN |
+
FlxBar.FILL_LEFT_TO_RIGHT |
+FlxBarFillDirection.LEFT_TO_RIGHT |
+
FlxBar.FILL_RIGHT_TO_LEFT |
+FlxBarFillDirection.RIGHT_TO_LEFT |
+
FlxBar.FILL_TOP_TO_BOTTOM |
+FlxBarFillDirection.TOP_TO_BOTTOM |
+
FlxBar.FILL_VERTICAL_INSIDE_OUT |
+FlxBarFillDirection.VERTICAL_INSIDE_OUT |
+
FlxBar.FILL_VERTICAL_OUTSIDE_IN |
+FlxBarFillDirection.VERTICAL_OUTSIDE_IN |
+
FlxG.html5
browser types:HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
HTML5FrontEnd.INTERNET_EXPLORER |
+FlxBrowser.INTERNET_EXPLORER |
+
HTML5FrontEnd.CHROME |
+FlxBrowser.CHROME |
+
HTML5FrontEnd.FIREFOX |
+FlxBrowser.FIREFOX |
+
HTML5FrontEnd.SAFARI |
+FlxBrowser.SAFARI |
+
HTML5FrontEnd.OPERA |
+FlxBrowser.OPERA |
+
Some static String constants have been changed to abstract enums. This is not a breaking changes, since the old String values are still compatible, but for the sake of type safety it is recommended to use the enum values instead:
+As with regular enums, the enum name may be omitted.
+FlxText
alignment:HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
text.alignment = "left"; |
+text.alignment = FlxTextAlign.LEFT; |
+
text.alignment = "center"; |
+text.alignment = FlxTextAlign.CENTER; |
+
text.alignment = "right"; |
+text.alignment = FlxTextAlign.RIGHT; |
+
FlxG.keys
keys:HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
FlxG.keys.anyPressed(["SPACE", "W"]) |
+FlxG.keys.anyPressed([FlxKey.SPACE, FlxKey.W]) |
+
FlxG.keys.anyPressed(["SPACE", "W"]) |
+FlxG.keys.anyPressed([SPACE, W]) |
+
FlxSprite
:HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
getScreenXY() |
+getScreenPosition() |
+
cachedGraphics |
+graphic |
+
resetFrameBitmaps() |
+removed (set dirty to true to regen graphic) |
+
getFlxFrameBitmapData() |
+updateFramePixels() |
+
loadGraphicFromTexture() |
+removed (assign a frames collection to frames ) |
+
loadRotatedGraphicFromTexture() |
+removed (assign a frames collection to frames ) |
+
FlxCamera
:HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
bounds |
+minScrollX , minScrollY , maxScrollX and maxScrollY |
+
setBounds() |
+setScrollBoundsRect() |
+
follow() 's Offset argument |
+removed | +
FlxTilemap
:loadMap()
has been split up into loadMapFromArray()
and loadMapFromCSV()
.
FlxGroup
:callAll()
and setAll()
have been removed - use forEach()
instead:
// 3.3.x
+group.setAll("scrollFactor", FlxPoint.get(0, 0));
+group.callAll("kill");
+
+// 4.0.0
+group.forEach(function(basic:FlxBasic)
+{
+ basic.scrollFactor.set(0, 0);
+ basic.kill();
+});
+
+flixel.input.gamepad
:The hardware IDs of the different controller types are now mapped to a common FlxGamepadInputID
. This avoids the need of having to handle multiple controller types - this now happens automatically under the hood.
// 3.3.x
+if (gamepad.pressed(XboxButtonID.A) ||
+ gamepad.pressed(OUYAButtonID.O) ||
+ gamepad.pressed(LogitechButtonID.TWO)) {}
+
+// 4.0.0
+if (gamepad.anyPressed([FlxGamepadInputID.A])) {}
+// or
+if (gamepad.pressed.A) {}
+
+It is still possible to use the IDs from the flixel.input.gamepad.id
classes via the functions with the Raw
suffix.
Because of the poor driver support, the PS3 ID class / support for PS3 controllers have been removed.
+FlxTimer
:Timers cannot be started right at construction anymore, instead you need to call start()
:
// 3.3.x
+new FlxTimer(time, onComplete, loops);
+
+// 4.0.0
+new FlxTimer().start(time, onComplete, loops);
+
+FlxPath
:FlxPath#start()
no longer takes a FlxObject
argument, instead, FlxObject
now has a path property. This means FlxObject
takes care of updating the path, taking care of the issue that paths are not paused along with the objects they work on in substates.
// 3.3.x
+var path = new FlxPath().start(object, points);
+
+// 4.0.0
+object.path = new FlxPath().start(points);
+
+FlxColor
/ FlxColorUtil
refactor:FlxColor
is now an abstract
, which means it can be used like an object, while the underlying type is still a regular Int
. The static FlxColorUtil
functions can now be used as member methods or properties:
// 3.3.x
+var color:Int = 0x008080;
+trace(FlxColorUtil.getGreen(color));
+
+// 4.0.0
+var color:FlxColor = 0x008080;
+trace(color.green);
+
+The amount of colors presets (FlxColor.RED
etc..) has been reduced.
FlxEmitter
refactor:FlxEmitterExt
has been merged into FlxEmitter
. For circular emitters, FlxEmitterMode.CIRCLE
can be used.
Most properties are now FlxRangeBounds
objects which have a min
and a max
FlxRange
object.
The separate color component Bounds
have been merged into a FlxRangeBounds<FlxColor>
.
HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
at() |
+focusOn() |
+
on |
+emitting |
+
FlxParticle
changes:Most properties are now FlxRange
objects which have a start
and an end
value (for example velocityRange
).
HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
maxLifespan |
+lifespan |
+
lifespan |
+age (counts up instead of down) |
+
FlxRandom
refactor:FlxRandom
can now be instantiated and the static functions are now member methods. A pre-created instance is available via FlxG.random
.
Some methods have also been renamed or removed:
+HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
FlxRandom.intRanged(min, max) |
+FlxG.random.int(min, max) |
+
FlxRandom.floatRanged(min, max) |
+FlxG.random.float(min, max) |
+
FlxRandom.chanceRoll(chance) |
+FlxG.random.bool(chance) |
+
FlxRandom.weightedGetObject() |
+removed (getObject() now has a range argument) |
+
FlxRandom.colorExt() |
+removed | +
FlxAngle
changes:HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
FlxAngle.getAngle(point1, point2) |
+point1.angleBetween(point2) |
+
FlxAngle.angleLimit() |
+removed (use FlxMath.bound() instead) |
+
There have been several changes to FlxAngle.rotatePoint()
:
FlxPoint
(rotate()
)// 3.3.x
+var angle = 45;
+var point = FlxPoint.get(10, 5);
+FlxAngle.rotatePoint(x, y, pivotX, pivotY, angle, point);
+
+// 4.0.0
+var angle = 45;
+var point = FlxPoint.get(10 + x, 5 + y);
+var pivot = FlxPoint.weak(pivotX, pivotY);
+point.rotate(pivot, angle);
+
+FlxMath
changes:HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
newAmount = FlxMath.wrapValue(value, amount, max + 1) |
+newAmount = FlxMath.wrapValue(value + amount, min, max) |
+
FlxMath.getDistance(point1, point2) |
+point1.distanceTo(point2) |
+
FlxMath.MIN_VALUE |
+FlxMath.MIN_VALUE_FLOAT |
+
FlxMath.MAX_VALUE |
+FlxMath.MAX_VALUE_FLOAT |
+
HaxeFlixel 3.3.x | +HaxeFlixel 4.0.0 | +
---|---|
import flixel.text.FlxTextField |
+import flixel.addons.text.FlxTextField |
+
FlxPoint#inFlxRect() |
+FlxPoint#inRect() |
+
FlxRect#containsFlxPoint() |
+FlxRect#containsPoint() |
+
flixel.plugin.MouseEventManager |
+flixel.input.mouse.FlxMouseEventManager |
+
flixel.util.loaders.CachedGraphics |
+flixel.graphics.FlxGraphic |
+
FlxArrayUtil.getRandom() |
+FlxG.random.getObject() |
+
FlxVelocity
's accelerateTowards*()
-functions now only take a single maxSpeed
argument (instead of x
and y
).
The complete
option of FlxTween
is now called onComplete
.
Not all flixel-addons changes are covered here. Please check the changelog for the rest.
+FlxNapeState
refactor:FlxNapeState
is now FlxNapeSpace
and no longer extends FlxState
. This makes it possible to use the flixel.addons.nape
package along with other FlxState
subclasses (for example FlxUIState
).
// flixel-addons 1.x.x
+import flixel.addons.nape.FlxNapeState;
+
+class PlayState extends FlxNapeState
+{
+ override public function create():Void
+ {
+ super.create();
+ }
+}
+
+// flixel-addons 2.0.0
+import flixel.FlxState;
+import flixel.addons.nape.FlxNapeSpace;
+
+class PlayState extends FlxState
+{
+ override public function create():Void
+ {
+ super.create();
+ FlxNapeSpace.init();
+ }
+}
+
+
+
+ Haxelib is a package manager and utility that comes with your Haxe install. Here are the most used commands, the full usage docs are available here.
+Install a Haxelib library from lib.haxe.org:
+haxelib install <library>
+
Install a Haxelib library from Git:
+haxelib git <library> <url> <branch>
+
Note: haxelib git
enables the development directory for that library, which silently prevents haxelib set
from working. Use haxelib dev <library>
to disable it.
Update your Haxelib libraries, including the ones from Git:
+haxelib upgrade
+
Change to different version of a library:
+haxelib set <library> <version>
+
Remove a library:
+haxelib remove <library>
+
To use a library in your HaxeFlixel project, add it to your project.xml
:
<haxelib name="<library>" />
+
+To make sure you are using the latest version of Haxelib you can run the selfupdate
command.
haxelib selfupdate
+
+
+ Visual Studio Code is an open-source, cross-platform, lightweight code editor by Microsoft. The Haxe extension seamlessly integrates with the compiler's IDE services and uses them for:
+You can find detailed documentation for the Haxe extension in the Wiki, this page focuses on the Flixel-specific parts.
+Go to the Extensions tab and install the Lime extension. This automatically installs the Haxe extension as well.
++
VSCode stores its project-specific settings in a .vscode
subfolder - flixel-tools can create one with sensible defaults for Flixel projects. Just select VSCode as your preferred editor during the setup
command, or add -ide vscode
to the command you're running.
Note: make sure you have the latest versions of flixel-tools and flixel-templates:
+haxelib update flixel-tools
+haxelib update flixel-templates
+
You have several options for creating projects with a .vscode
configuration:
Create a new, empty project:
+ flixel template -n "VSCodeTest" -ide vscode
+
Create a new project based on one of the demos:
+ flixel create -ide vscode
+
Add the .vscode
folder to a project that already exists, for instance the current working directory:
flixel configure . -ide vscode
+
Add VSCode config files to an entire directory of projects, like flixel-demos:
+ flixel configure C:\HaxeToolkit\haxe\lib\flixel-demos\git -ide vscode
+
Once you've installed the Lime extension and have a project with a .vscode
folder, just open it with File
-> Open Folder
. If the workspace was correctly detected as a Lime project (needs a Project.xml
file), you should notice these dropdown menus appearing in the status bar:
Code completion features should work out of the box now:
+ +If you're having trouble, please refer to the Haxe extension's Troubleshooting guide.
+Building and running your projects in VSCode is done through tasks. You can view the list of available tasks via Tasks
-> Run Task...
:
To build and run your project, select the lime: test
task. With Flixel's template, this is configured as the default build task, so you can also invoke it directly via Tasks
-> Run Build Task...
or by pressing Ctrl+Shift+B
:
You may want to assign a shortcut to Run Task...
or change the shortcut for Run Build Task...
to something more convenient such as F5
. You can do so in File
-> Preferences
-> Keyboard Shortcuts
.
Finally, you can change the current configuration (combination of target and Debug/Release/Final) by using the dropdown menu in the status bar:
+ +By default, compiler errors and warnings are shown in the Terminal view at the bottom of the screen. You can navigate to the source of the error via Ctrl
+Click
on the file path:
Alternatively, you can switch to the Problems tab which has a nicer presentation. It shows errors / warnings from compilation as well as diagnostics that are updated each time you save a file:
+ +The .vscode
template from flixel-tools already includes the launch.json
needed for debugging. The Lime "Build + Debug" / "Debug" launch configurations support debugging with the following targets / extensions:
Simply "Start Debugging" with the "Build + Debug" launch configuration (make sure the "Debug" configuration for the target you want to debug is selected in the status bar). This first builds the project and then starts the debugger.
+ +Here's what it should look like when you hit a breakpoint:
+ +There is also a Macro launch configuration for debugging Haxe Macros.
+ + +This website is built using the Docpad system which compiles the files to a static site on a community sponsored host. +Extensions and help with improving the website and our documentation are greatly appreciated.
+These documentation pages are an open-source project for the community to improve and extend. Notice the Edit button in the top right hand corner of every page. +This will link you right to the GitHub page of the documentation where you can fork and edit the pages right in the GitHub editor. +Anyone of the HaxeFlixel team can merge pull requests and changes will be uploaded to our host.
+Everything is written in the GitHub flavoured markdown format. You can see the files written in markdown from the extension *.html.md
.
The markdown format is parsed through marked with the GitHub markdown syntax enabled. +You can see a guide for the markdown syntax here.
+ + +If you have successfully compiled a Hello World sample, there are multiple places to jump to from here:
+Also be sure to get in touch with the community if you have any questions along the way!
+Furthermore, this documentation and website are open source and we welcome additions from users like you. :)
+ + +The original ActionScript 3 version of Flixel has proven to be an effective codebase to make 2D games quickly and easily for the Adobe Flash and AIR runtimes. Haxe and the Open Flash Library provide significant enhancements and opportunities that ActionScript 3, AIR and Flash are limited by. Haxe and the OpenFL offers more efficient and open source native runtimes to your code base that ActionScript 3 and Adobe Flash/Air are not able to provide. HaxeFlixel still retains the same Flash Player target, but you gain a familiar, yet superior open source language Haxe.
+Haxe as a toolkit and language provides features today that ActionScript developers have been seeking for years (ActionScript 4, cancelled code name "Next" compiler). The language ActionScript 3, although proven to be effective and easy to learn, can be a barrier itself.
+The Haxe language offers a familiar syntax whilst being able to cross-compile to different target languages such as native C++, Neko, HTML5, PHP, Java and more. For example OpenFL is letting you target Linux natively where Adobe has discontinued their AIR support some time ago.
+Haxe still lets you target Flash and AIR runtimes, whilst also opening the doors to much more. You gain the benefits of a superior language and open source runtime. For example, C++ works with OpenFL and LibSDL, which would not be possible with Adobe's workflow. HaxeFlixel is far from the only ActionScript library being ported to Haxe. HaxePunk and other projects can be found on GitHub and haxelib.
+Haxe is a cross-compiled language which is unlike solutions such as Adobe Air that use a virtual machine to run on different platforms. When you compile your Haxe code to a native target, it is translated into the native language itself, such as C++. Native code running on your target runtime does not share the same overheads in performance that a virtual machine has, so your code is often much faster in comparison. HaxeFlixel also takes advantage of OpenFL's use of GPU accelerated rendering through their drawTiles api.
+By using the Haxe Toolkit and OpenFL, advanced developers can extend and modify anything in the toolchain right down from the compiler and the native runtime code such as HXCPP and SDL. The developers and the community behind these tools have shown a constant improvement to the projects over many years. History has shown commercial solutions do not always prioritize developer requests, and even advanced developers can't just fix their issues or bugs they find themselves.
+The source code for everything in the toolchain is available on GitHub:
+ + + +When you run your game now, you'll notice that everything is really, really small. We're going to make it more visible by applying some zoom to our game so that we can get a better look at the action. This is a pretty quick change:
+Open the Main.hx
file. This class initializes your game, and there are a few things here that you could play with, if you want. We just need to change a couple of the arguments.
Change the width and height arguments of new FlxGame()
to 320
and 240
(half of their original values).
If you ran the game right now, everything would be zoomed in, but the player would run off the screen pretty quick - we need to tell the camera to follow the player around. So, open the PlayState
again.
In the create function, after you add the player and before super.create()
, add:
FlxG.camera.follow(player, TOPDOWN, 1);
+
+ This simply tells the camera to follow the player using the TOPDOWN
style, with a lerp
of 1 (which helps the camera move a little more smoother).
That's it! Test out the game and see how it looks!
+ +Next, let's give the player something to pickup around the map!
+ + +Create cross-platform games easier and free.
All with one codebase.
+ Getting Started + GitHub +
+ +Cross-compile your games natively to:
+ +Browse and learn from our 77 demos:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++ Haxeflixel's official sponsors made siginificant contributions during our fundraising efforts. + Read more about the community backers in the fundraiser blog post. + Sponsors are ordered by their contribution amount, starting with the highest category Platinum. +
+ +