Trouble with inheritance in HaxeFlixel game

82 views
Skip to first unread message

Paolo Mazzon

unread,
Nov 14, 2015, 1:06:36 AM11/14/15
to Haxe
Enter code here...

Simply put, I have two classes that do all the work in my game that inherit the Flixel Sprite class, as well as the Flixel state class. Their respective names are GameObject and GameRoom. I have a child of GameRoom: PlayState, and it has two variables that GameRoom doesn't; those being oPlayer and oWalls. oWalls is the problem at hand. It's a Flixel group. What I'm trying to do is from oPlayer, use oWalls in FlxG.collide, but the compiler says it's not a variable of GameRoom. Here is the code:

This is the player's frame event/update code, inherited from the GameObject class.
override public function frameEvent(backEnd:GameRoom)
{
 
// Set the velocity -- not the coordinates. We do that at the end.
  hsp
= (-boolToInt(FlxG.keys.pressed.LEFT) + boolToInt(FlxG.keys.pressed.RIGHT)) * moveSpeed;
  vsp
= (-boolToInt(FlxG.keys.pressed.UP)   + boolToInt(FlxG.keys.pressed.DOWN))  * moveSpeed;
  x
+= hsp;
  y
+= vsp;
 
 
 
FlxG.collide(this, backEnd.oWalls);
}

The base class's function doesn't have any code, it's all done by the child.

This is where the GameRoom class loops through all game objects to do their frame event.
override public function update():Void
{
 
// Do all the frame events in the game objects
 
for (i in gameObjects)
 
{
    i
.frameEvent(this);
 
}

 
super.update();
}

The actual PlayState doesn't override it, because it doesn't need to. The whole reason for this is that it's portable across all levels, so no matter the state/level, as long as the room inherits GameRoom, it will have the same structure.

So I get this error
source/ObjPlayer.hx:36: characters 21-35 : GameRoom has no field oWalls

I know it's because I use backEnd:GameRoom as a parameter, so it will only consult GameRoom, but it is being passed the child object, not GameRoom. Even when I put the for loop inside PlayState, guaranteeing that it is being passed the child, PlayState, and not GameRoom, I got the same error. So then, the question is how do I pass the child object to it instead of the base class?

Any help is greatly appreciated.

--This is the end of the post, but if you need all the code for everything, here it is. I know it's unformatted, I don't know what happened--

PlayState.hx
import flixel.group.FlxGroup;


class PlayState extends GameRoom
{
 
// Game objects
 
public var oPlayer:ObjPlayer;


 
// The walls in the room
 
public var oWalls:FlxGroup;


 
//Function that is called up when to state is created to set it up.
 
override public function create():Void
 
{
 
super.create();


 
// Populate the load map
 addLoadClass
("#", ObjWall, oWalls);


 oPlayer
= new ObjPlayer(32, 32, this);


 
// Load the game room
 loadFile
(16, 16, "assets\\Level.txt", 0, 0);
 
}
}

ObjPlayer.hx
import flixel.FlxG;


class ObjPlayer extends GameObject
{
 
// The player's movement speed in pixels
 
var moveSpeed:Float;


 
// The velocity in pixels for collision checking
 
var hsp:Float;
 
var vsp:Float;


 
override public function create(newX:Int, newY:Int, backEnd:GameRoom)
 
{
 
// Just load the player's graphic
 loadGraphic
(AssetPaths.player__png);


 moveSpeed
= 3;
 
}


 
override public function frameEvent(backEnd:GameRoom)
 
{
 
// Set the velocity -- not the coordinates. We do that at the end.
 hsp
= (-boolToInt(FlxG.keys.pressed.LEFT) + boolToInt(FlxG.keys.pressed.RIGHT)) * moveSpeed;
 vsp
= (-boolToInt(FlxG.keys.pressed.UP)   + boolToInt(FlxG.keys.pressed.DOWN))  * moveSpeed;
 x
+= hsp;
 y
+= vsp;


 
FlxG.collide(this, backEnd.oWalls);
 
}


 
// Convert a boolean value to an int (1 or 0)
 
private function boolToInt(bool:Bool)
 
{
 
if (bool) return 1; else return 0;
 
}
}

GameRoom.hx
import flixel.FlxState;
import flixel.group.FlxGroup;
import sys.io.File;


class GameRoom extends FlxState
{
 
// The list of all the events to do
 
public var gameObjects:Array<GameObject>;


 
// The child class must fill this map to use the room loader
 
var loadMap:Map<String, Class<GameObject>>;
 
var loadMapGroup:Map<String, FlxGroup>;


 
override public function create():Void
 
{
 
super.create();


 
// Create the empty array for game objects
 gameObjects
= [];


 
// Create an empty map for loadMap
 loadMap      
= new Map<String, Class<GameObject>>();
 loadMapGroup
= new Map<String, FlxGroup>();
 
}


 
// Function that is called when the game is over
 
override public function destroy():Void
 
{
 
// Do all the destroy events in the game objects
 
for (i in gameObjects)
 
{
 i
.destroyEvent(this);
 
}


 
super.destroy();
 
}


 
// Really obvious frame events
 
override public function update():Void
 
{
 
// Do all the frame events in the game objects
 
for (i in gameObjects)
 
{
 i
.frameEvent(this);
 
}


 
super.update();
 
}


 
// Add a class to the load map
 
function addLoadClass(char:String, loadClass:Class<GameObject>, useGroup:FlxGroup):Void
 
{
 loadMap
[char] = loadClass;
 loadMapGroup
[char] = useGroup;
 
}


 
// Loads a file into a room
 
function loadFile(tileWidth:Float, tileHeight:Float, fname:String, xOffset:Int, yOffset:Int):Void
 
{
 
// First we load the file into a string
 
var workingFile:String = File.getContent(fname);


 
// These are the x/y inside the loop
 
var workingX:Int = 0;
 
var workingY:Int = 0;


 
// Now we loop through each character
 
for (i in 0...workingFile.length)
 
{
 
// Put an object here is it wasn't a space
 
if (workingFile.charAt(i) != " " && loadMap.exists(workingFile.charAt(i)))
 
{
 
var tempClass = Type.createInstance(loadMap[workingFile.charAt(i)], [xOffset + workingX * tileWidth, yOffset + workingY * tileHeight, this]);


 
// Add the object to a group if applicable
 
if (loadMapGroup[workingFile.charAt(i)] != null)
 
{
 loadMapGroup
[workingFile.charAt(i)].add(tempClass);
 
}
 
}


 
// Check if we are at the end of a line
 workingX
++;
 
if (workingFile.charAt(i) == "\n")
 
{
 workingX
= 0;
 workingY
++;
 
}
 
}
 
}
}


And last, GameObject.hx
import flixel.FlxSprite;


class GameObject extends FlxSprite
{
 
// This class basically just has stubs for a frame, and create
 
public function new(newX:Int, newY:Int, backEnd:GameRoom)
 
{
 
super(newX, newY);
 create
(newX, newY, backEnd);


 
// Add this object to the backEnd
 backEnd
.gameObjects.insert(backEnd.gameObjects.length, this);
 backEnd
.add(this);
 
}


 
// This is for the child class
 
public function create(newX:Int, newY:Int, backEnd:GameRoom)
 
{
 
// All for the child class...
 
}


 
public function frameEvent(backEnd:GameRoom)
 
{
 
// Nothing...
 
}


 
public function destroyEvent(backEnd:GameRoom)
 
{
 
// Nothing...
 
}
}


Sorry about the code dump, but I know on Stack Overflow, people go nuts if you don't post all the code.

Raoul Duke

unread,
Nov 14, 2015, 1:16:49 AM11/14/15
to haxe...@googlegroups.com
It seems to me you have to understand how static typing, and runtype
typing work. The static type in
override public function frameEvent(backEnd:GameRoom)
means that you are telling the compiler to limit backEnd's abilities
to that of a GameRoom, and not any of the subclasses. That's the whole
point.

If you want to keep this approach then you might have to / be able to
do a 2nd override in the PlayState class.

something like

1) remove "FlxG.collide(this, backEnd.oWalls);" from GameRoom.frameEvent().

2) add this override in PlayState.
override public function frameEvent(backEnd:PlayState)
{
super.frameEvent(backEnd);
FlxG.collide(this, backEnd.oWalls);
}

Paolo Mazzon

unread,
Nov 14, 2015, 1:50:53 AM11/14/15
to Haxe
I'm sorry, but I really don't get what you're saying. Add an override for frameEvent in PlayState? GameRoom doesn't have a FrameEvent, GameObject and ObjPlayer do. And if I make the backEnd PlayState, that removes all portability because now that entire class is only compatible with that state. The question was how do I access the sub class of GameRoom, not just change the parameters.

Thanks for the reply though.

David Holaň

unread,
Nov 14, 2015, 5:02:07 AM11/14/15
to haxe...@googlegroups.com

--
To post to this group haxe...@googlegroups.com
http://groups.google.com/group/haxelang?hl=en
---
You received this message because you are subscribed to the Google Groups "Haxe" group.
For more options, visit https://groups.google.com/d/optout.

Paolo Mazzon

unread,
Nov 14, 2015, 1:21:52 PM11/14/15
to Haxe
Sadly, no. Even after I cast it (I tried two methods) it still didn't allow access to the fields only PlayState held.

Raoul Duke

unread,
Nov 14, 2015, 6:21:26 PM11/14/15
to haxe...@googlegroups.com
I suggest you are missing the fundamentals about how inheritance, and
static type, and runtime types, all work. It will be hard for anybody
to get the point across. If you can find somebody local to sit down
with and talk in person, I think that might be the best way for you to
effectively work through confusion / missing understanding. Best
wishes.

Paolo Mazzon

unread,
Nov 14, 2015, 8:02:20 PM11/14/15
to Haxe


On Saturday, November 14, 2015 at 2:02:07 AM UTC-8, David Holaň wrote:
Sadly, I am mistaken. Long story short, David's method does work. Thanks man.

And @Raoul, I understand what statically typed is, and I get what you were saying; but you didn't read the part where I wanted that class to be portable. What you suggested was the most obvious solution that anyone could have worked out. But it was far from portable -- which was the problem. Try to read the whole post before you start passively-aggressively commenting against the OP.

Raoul Duke

unread,
Nov 14, 2015, 10:08:37 PM11/14/15
to haxe...@googlegroups.com
my sincerest apologies for trying to help in any way.
Reply all
Reply to author
Forward
0 new messages