A more practical / easier way to switch (walk through different) maps?

64 views
Skip to first unread message

JustGabe

unread,
Mar 14, 2016, 1:39:29 AM3/14/16
to HaxeFlixel


So far, none of the tutorials I've found have truly addressed how to change maps or levels, and I only got a glimpse of how to do it via an open source game from a Game Jam.
Anyway, it came to me that the best way to reset everything is just by switching into a new PlayState and having the variables taken from a Registry.hx: the height, width, images of the map, the amount and locations of NPC's, their respective dialogues and the exit tiles as well as the new location of the char in the new map.  However I feel that maybe there could be a better way to simplify it. Here's the whole code:

Testing State:
class Testing extends FlxState
{
   
//Map
   
public static var TS:Int = 40;
 
private var map:FlxTilemap;
     
private var currentMap:Int = Reg.currentMap;
   
       
//Player
       
private var char:Player;
       
private var posInMap:FlxPoint;
 
       
//NPCs
 
public static var NPCs:Array<NPC>;
     
private var currentNPC:NPC;
     
       
//Dialogue text
 
private var textBox:FlxSprite;
 
private var text:FlxText;
       
private var dialogueIndex:Int = 0;
     
       
//Others
       
//while the player is talking to other chars the games freezes
 
public static var freezed:Bool = false;
 
       
override public function create():Void
 
{
               
super.create();
         
FlxG.camera.fade(0xff000000, 0.25, true);
               
               
//Setting the sounds
           
if (Reg.mapChanged)
             
{
                       
FlxG.sound.playMusic(Reg.music[Reg.mapIndex]);
                 
Reg.mapChanged = false;
         
}
               
               
//Setting the Map
               map
= new FlxTilemap();
         map
.loadMapFromArray(Reg.map[currentMap], Reg.mapWidth[currentMap], Reg.mapHeight[currentMap], Reg.mapTiles[Reg.mapIndex], TS, TS, null, 0, 0);
         add
(map);
               
               
//Setting the Character
         
char = new Player(Reg.charPos.x * TS, Reg.charPos.y * TS, map);
         
char.dir = Reg.charDir;
         add
(char);
             
               
//Setting the NPC's
             
NPCs = new Array();
             
for (i in 0...Reg.chars[currentMap].length)
             
{
                       
var charType:Int = Reg.chars[currentMap][i][0];
                 
var npc:NPC = new NPC(Reg.chars[currentMap][i][1], Reg.chars[currentMap][i][2], Reg.charType[charType], Reg.dialogues[currentMap][i]);
                 
NPCs.push(npc);
                 add
(npc);
               
}
               
               
//Setting the text
              textBox
= new FlxSprite(0, 0);
          textBox
.makeGraphic(FlxG.width, TS * 2, FlxColor.GREEN);
                textBox
.alpha = 0.75;
           textBox
.visible = false;
                add
(textBox);
           text
= new FlxText(10, 10, FlxG.width - 20);
            text
.setFormat("", 16);
         text
.visible = false;
           add
(text);
     
}


       
override public function update(elapsed:Float):Void
     
{
               
super.update(elapsed);
         
               
if (!freezed)
           
{
                       
//Player movement
                       
if (FlxG.keys.anyPressed(["DOWN", "S"]))
                       
{
                               
char.move("DOWN");
                     
}
                       
else if (FlxG.keys.anyPressed(["UP", "W"]))
                     
{
                               
char.move("UP");
                       
}
                       
else if (FlxG.keys.anyPressed(["LEFT", "A"]))
                   
{
                               
char.move("LEFT");
                     
}
                       
else if (FlxG.keys.anyPressed(["RIGHT", "D"]))
                 
{
                               
char.move("RIGHT");
                     
}
                       
else if (FlxG.keys.justPressed.SPACE) //Talk to chars
                   
{
                               
if (!char.checkChar(char.dir))
                         
{
                                       freezed
= true;
                                 textBox
.visible = true;
                                 text
.visible = true;
                                    currentNPC
= char.getNPC(char.dir);
                                     text
.text = currentNPC.dialogues[dialogueIndex];
                               
}
                       
}
               
}
               
else
           
{
                       
if (FlxG.keys.justPressed.SPACE) //Is talking with an NPC
                       
{
                               dialogueIndex
++;
                               
if (dialogueIndex < currentNPC.dialogues.length)
                               
{
                                       text
.text = currentNPC.dialogues[dialogueIndex];
                               
}
                               
else
                           
{
                                       freezed
= false;
                                        textBox
.visible = false;
                                        text
.visible = false;
                                   dialogueIndex
= 0;
                             
}
                       
}
               
}
       
}
       
}


Reg.hx
class Reg
{
//The direction the char faces when the new map loads
public static var charDir:String = "DOWN";
//Where the char is positioned when the map loads
public static var charPos:FlxPoint = new FlxPoint(2, 2);
public static var mapIndex:Int = 0;
public static var mapChanged:Bool = true;
public static var mapTiles:Array<String> = [
"assets/images/tiles001.png",
"assets/images/tiles002.png"
];
public static var music:Array<String> = [
"assets/music/Super_Mario_RPG_OST_-_35_Welcome_to_Bukki_Tower.ogg",
"assets/music/Super_Mario_RPG_OST_-_09_The_Road_Is_Full_of_Dange.ogg"
];
 
public static var currentMap:Int = 0;
public static var map:Array<Array<Int>> = [
[2, 2, 2, 2, 2, 0,
2, 1, 1, 1, 2, 0,
2, 1, 1, 1, 1, 0,
2, 1, 1, 1, 2, 0,
2, 2, 1, 2, 2, 0,
0, 0, 0, 0, 0, 0],
[0, 2, 2, 2, 2, 2,
0, 2, 1, 1, 1, 2,
0, 1, 1, 1, 1, 2,
0, 2, 1, 1, 1, 2,
0, 2, 2, 1, 2, 2,
0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 0,
2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0],
[0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 2, 0,
0, 1, 1, 1, 1, 0,
0, 0, 1, 1, 1, 0,
0, 3, 3, 3, 3, 3]
];
public static var mapWidth:Array<Int> = [6, 6, 11, 6];
public static var mapHeight:Array<Int> = [6, 6, 6, 5];
/* pos 0 and 1, are for exits in X and Y, pos 2 is the destination map
* pos 3 and 4 are the new pos in x and y in the new map and pos 5 
* is the type of tiles the map is composed of
*/ 
public static var mapExits:Array<Array<Array<Int>>> = [
[[4, 2, 1, 2, 2, 0], [2, 4, 2, 2, 2, 0]],
[[1, 2, 0, 3, 2, 0], [3, 4, 2, 7, 2, 0]],
[[2, 1, 0, 2, 3, 0], [7, 1, 1, 3, 3, 0], [9, 3, 3, 2, 2, 1]],
[[1, 2, 2, 8, 3, 0]]
];
/* an array that holds all the NPCs in a map,
* the pos 0 is the type of sprite of the NPC,
* pos 1 and 2 are the X and Y in the tilemap
*/
public static var chars:Array<Array<Array<Int>>> = [
[[0, 3, 1]],
[[1, 4, 1]],
[[2, 4, 2], [3, 5, 2], [1, 1, 2]],
[]
];
public static var dialogues:Array<Array<Array<String>>> = [
[["Hello!", "How are you?"]],
[["Sup!", "Need any help?", "You seem fine thou."]],
[["How do we get out of here?", "I feel like having a sun bath"], ["This places seems nice", "What do you think?"], ["..."]],
[]
];
public static var charType:Array<String> = [
"assets/images/npc_001.png",
"assets/images/npc_002.png",
"assets/images/npc_003.png",
"assets/images/npc_004.png",
];
}

and Player:
class Player extends FlxSprite
{
private var TS:Int = Testing.TS;
public var moveSpeed:Int = 2;
public var moving:Bool = false;
public var dir:String = "UP"; //the direction the char is facing
public var distance:Int = 0;
public var map:FlxTilemap;
public function new(x:Float, y:Float, map:FlxTilemap) 
{
super(x, y);
this.map = map;
loadGraphic("assets/images/npc_002.png", true, TS, TS);
//Setting the idle animations
animation.add("IDLE_DOWN", [0]);
animation.add("IDLE_UP", [3]);
animation.add("IDLE_LEFT", [6]);
animation.add("IDLE_RIGHT", [9]);
//Setting the walk animations
animation.add("WALK_DOWN", [1, 2], 12);
animation.add("WALK_UP", [4, 5], 12);
animation.add("WALK_LEFT", [7, 8], 12);
animation.add("WALK_RIGHT", [10, 11], 12);
path = new FlxPath();
}
override public function update(elapsed:Float):Void
{
super.update(elapsed);
if (!moving)
{
look();
}
else
{
if (distance > 0)
{
switch(dir)
{
case "UP": y -= 4;
case "DOWN": y += 4;
case "LEFT": x -= 4;
case "RIGHT": x += 4;
}
distance -= 4;
}
else
{
var cm:Int = Reg.currentMap;
/*
* In the currentMap value, we check that the X and Y of the player after moving is the same as
* the exit points (value 0 and 1), if so, the mapNum takes the value of the 3rd (value 2) number
* and the player's initial position will be the value of the 4th (for x) and 5th (for y) numbers
*/
for (i in 0...Reg.mapExits[cm].length)
{
if (Std.int(x / TS) == Reg.mapExits[cm][i][0] && Std.int(y / TS) == Reg.mapExits[cm][i][1])
{
Reg.currentMap = Reg.mapExits[cm][i][2];
Reg.charPos.x = Reg.mapExits[cm][i][3];
Reg.charPos.y = Reg.mapExits[cm][i][4];
//we check if the "zone" changed, if it's diff, the music will fade
if (Reg.mapIndex != Reg.mapExits[cm][i][5])
{
Reg.mapIndex = Reg.mapExits[cm][i][5];
Reg.mapChanged = true;
}
FlxG.camera.fade(FlxColor.BLACK, 0.25, false, changeLevel);
}
}
moving = false;
}
}
}
private function look():Void
{
switch(dir)
{
case "UP": animation.play("IDLE_UP");
case "DOWN": animation.play("IDLE_DOWN");
case "LEFT": animation.play("IDLE_LEFT");
case "RIGHT": animation.play("IDLE_RIGHT");
}
}
public function move(moveDir:String):Void
{
if (distance == 0 && !moving)
{
dir = moveDir;
if (checkWall(moveDir) && checkChar(moveDir))
{
moving = true;
distance = TS;
switch(dir)
{
case "UP": animation.play("WALK_UP");
case "DOWN": animation.play("WALK_DOWN");
case "LEFT": animation.play("WALK_LEFT");
case "RIGHT": animation.play("WALK_RIGHT");
}
}
}
}
public function checkWall(moveDir:String):Bool
{
var nextTile:FlxPoint = new FlxPoint();
nextTile.x = getNextTile(moveDir).x / TS;
nextTile.y = getNextTile(moveDir).y / TS;
var tile:Int = map.getTile(Std.int(nextTile.x), Std.int(nextTile.y));
switch (tile)
{
case 0, 2, 3: return false;
}
//check if the char is out of bounds
if (map.getTile(Std.int(nextTile.x), Std.int(nextTile.y)) == null)
return false;
return true;
}
private function getNextTile(moveDir:String):FlxPoint 
{
var nextTile:FlxPoint = new FlxPoint(x, y);
switch(moveDir)
{
case "UP": nextTile.y -= TS;
case "DOWN": nextTile.y += TS;
case "LEFT": nextTile.x -= TS;
case "RIGHT": nextTile.x += TS;
}
return nextTile;
}
public function checkChar(moveDir:String):Bool
{
var nextTile:FlxPoint = new FlxPoint();
nextTile.x = getNextTile(moveDir).x;
nextTile.y = getNextTile(moveDir).y;
//Check if there's collision with other chars
for (i in 0...Testing.NPCs.length)
{
if (nextTile.x == Testing.NPCs[i].x && nextTile.y == Testing.NPCs[i].y)
return false;
}
return true;
}
public function getNPC(dir:String):NPC
{
var next:FlxPoint = new FlxPoint(0, 0);
next = getNextTile(dir);
for (i in 0...Testing.NPCs.length)
{
if (next.x == Testing.NPCs[i].x && next.y == Testing.NPCs[i].y)
return Testing.NPCs[i];
}
return null;
}
public function changeLevel():Void
{
/* to make sure the char is facing the right direction, the charDir will have the same value as dir
* before changing states
*/
Reg.charDir = dir;
moving = true;
FlxG.switchState(new Testing());
}
}


Ashiq A.

unread,
Mar 14, 2016, 6:40:08 AM3/14/16
to haxef...@googlegroups.com
Reg is supposed to be used for global data that persists across multiple states (eg. player score, or maybe your party members).

Have you considered creating a "map" class to encapsulate all map-related functionality?

Have you thought about using a data-driven approach where you keep map data in JSON files or something similar?

--
HaxeFlixel Development Community
See our github https://github.com/haxeflixel/ and our documentation http://haxeflixel.com/documentation/
---
You received this message because you are subscribed to the Google Groups "HaxeFlixel" group.
To unsubscribe from this group and stop receiving emails from it, send an email to haxeflixel+...@googlegroups.com.
Visit this group at https://groups.google.com/group/haxeflixel.
To view this discussion on the web visit https://groups.google.com/d/msgid/haxeflixel/598ad8be-7e49-4fa6-9efb-a237a3cfb1c6%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

JustGabe

unread,
Mar 15, 2016, 3:50:48 PM3/15/16
to HaxeFlixel
El lunes, 14 de marzo de 2016, 4:40:08 (UTC-6), Ashiq A. escribió:

Have you thought about using a data-driven approach where you keep map data in JSON files or something similar?


Can you tell me more of that approach please?

Ashiq A.

unread,
Mar 15, 2016, 3:59:34 PM3/15/16
to haxef...@googlegroups.com
Yes. Let me make this extremely practical and relevant for you.

In your Reg class, you currently have a variable named "map" which is an array of arrays of ints. If I'm right, each array represents a map.

Instead of this, why not dump your map data into a text file. and load it at runtime? Instead, store a list of map names in an array.

Sample code:

```
var maps:Array<String> = ["tutorial-map.txt", "small-cave.txt"]; // not needed?

private function loadMap(name:String):Array<Int> {
  var contents:String = sys.io.File.getContent('assets/data/maps/${name}');
  var contentsAsArray:Array<Int> = ... // parse contents, populate array
  return contentsAsArray;
}
```

Your file names could be whatever format you want. A comma-separated list of integers may work.

You can also look into Tiled. You can create maps in Tiled using their (GUI) editor, and export it into several different formats. I think HaxeFlixel comes bundled with some code to read/parse Tiled maps already.

This is just maps; the same idea can extend to other parts of your game, too. In general, you want to separate data from code as much as possible. The best benefit of this is that you can do things like update maps and re-run your game without recompiling it.



--
HaxeFlixel Development Community
See our github https://github.com/haxeflixel/ and our documentation http://haxeflixel.com/documentation/
---
You received this message because you are subscribed to the Google Groups "HaxeFlixel" group.
To unsubscribe from this group and stop receiving emails from it, send an email to haxeflixel+...@googlegroups.com.
Visit this group at https://groups.google.com/group/haxeflixel.
Reply all
Reply to author
Forward
0 new messages