Building with macro in HaxeFlixel



  • Hey,
    I'm trying to build some stuff loaded and converted from a json file. And I get some weird errors like:

    openfl.Assets has no field getText (getText must be inside a #if macro ... #end)

    You cannot use @:build inside a macro : make sure that your enum is not used in macro (from AssetPaths)

    It takes me hours to attempt to implement some macros and I think it's really not worth it.

    Here's the macro I use:

    class UpgradesMacro {
    	#if macro
    	public macro static function build():Array<Field> {
    		var upgrades:Array<Upgrade> = Reflect.getProperty(Upgrades, "_upgrades");
    		var varNames:Array<String> = Reflect.getProperty(Upgrades, "_varNames");
    		var fields = Context.getBuildFields();
    		
    		for (i in 0...upgrades.length) {
    			var upgrade:Upgrade = upgrades[i];
    			var varName:String = varNames[i];
    			var pos = Context.currentPos();
    			
    			var getterFunc:Function = {
    				expr: macro return Upgrade.getUpgrade(varName),
    				ret: (macro:Upgrade),
    				args: []
    			};
    			
    			var propertyField:Field = {
    				name:varName,
    				access: [Access.APublic, Access.AStatic],
    				kind: FieldType.FProp("get", "null", getterFunc.ret),
    				pos: pos,
    			};
    			
    			var getterField:Field = {
    				name: 'get_$varName',
    				access: [Access.APrivate, Access.AStatic],
    				kind: FieldType.FFun(getterFunc),
    				pos: pos,
    			}
    			
    			fields.push(propertyField);
    			fields.push(getterField);
    		}
    		return fields;
    	}
    	#end
    }
    


  • First variant: you should do separate version of your class for macro, that doesn't use Flash/OpenFl. Otherwise it will complain that you're using macro inside flash. In your case you should use Haxe's functions for reading files, not OpenFl's. Here's an example (I don't read files though):

    package objects;
    
    #if !macro
    import flixel.math.FlxPoint;
    import flixel.FlxSprite;
    import flixel.FlxObject;
    import flixel.addons.util.FlxFSM;
    #end
    
    @:autoBuild(MedvedMacro.RegisterSpriteType())
    // this is needed for avoiding DCE, but maybe Compiler.include("objects") below already deals with it ??
    @:keepSub
    class BaseObject #if !macro extends FlxSprite #end
    {
    	public function new() 
    	{
    		#if !macro
    		super();
    		// other constructor stuff
    		#end
    	}
    	
    	#if !macro
            // other class stuff
    	#end
    }
    
    

    Second variant: define a separate class, that will contain all functions generated by macro. You need only those directives then:

    #if !macro @:build(MedvedMacro.GenerateSpriteDictionary()) #end
    class MedvedMacro
    {
    	 // intermediate variable, which value is preserved between class builds, but reinitialized at runtime
    	 static var classNameByAnimationName: Map<String, String> = new Map<String, String>();
    
    	 macro static public function RegisterSpriteType(): Array<Field>
    	 {
    		// stuff for building class BaseObject
    	 }
    	
    	macro static public function GenerateSpriteDictionary(): Array<Field>
    	{
    		// build package 'objects' now, this is needed since macro build order is undefined
    		Compiler.include("objects");
    
    		// stuff for building class MedvedMacro
    	}
    }
    

    I use both in combination.
    Even simple stuff required me about three days to do, but after all macros is one of main powers of Haxe, so it's worth to learn ;-)



  • Still can't figure it out how to load a text file using Haxe functions in Flash :/



  • @DleanJeans It doesn't matter much, whether you're targeting Flash or something else. The code is running on macro target - that is, at compile time. For some reasons, OpenFl guys included protection against using macro target, so you have to work around it.

    Here is a working example of reading a file that I hope will help:

    package;
    
    #if !macro
    import flixel.FlxG;
    import flixel.FlxSprite;
    import flixel.FlxState;
    import flixel.text.FlxText;
    import flixel.ui.FlxButton;
    import flixel.math.FlxMath;
    #end
    
    #if macro
    import sys.io.File;
    #end
    
    class MacroClass
    {
    	macro static public function getText(path: String)
    	{
    		var text: String = File.getContent(path);
    		trace(path);
    		trace(text);
    		return macro $v{text};
    	}
    }
    
    #if !macro
    class PlayState extends FlxState
    {
    	var text: FlxText;
    	
    	override public function create():Void
    	{
    		super.create();
    		
    		text = new FlxText(0, 0, 100, MacroClass.getText("Project.xml"), 12);
    		add(text);
    	}
    
    	override public function update(elapsed:Float):Void
    	{
    		super.update(elapsed);
    	}
    }
    #end
    

    Note that those traces are written to your output at compile time. Actually the whole function getText() is inlined to a string with file's contents.



  • You can't use sys.io.File in Flash



  • @DleanJeans Believe me, you can't use it on Flash, but you can for sure use it on macro. Including cases when you target Flash ;-)



  • Still can't. We should discuss about this on Slack or Discord.



  • @DleanJeans Did you try my exact example without your additional code? Also note that when you create a new project with HaxeFlixel template, your first state is MenuState (change it to PlayState).


Log in to reply
 

Looks like your connection to HaxeFlixel was lost, please wait while we try to reconnect.