A Flash Developer Resource Site

Results 1 to 7 of 7

Thread: [RESOLVED] Need Help With Character Movement/Blitting

  1. #1
    Member
    Join Date
    May 2008
    Location
    Ohio, USA
    Posts
    63

    resolved [RESOLVED] Need Help With Character Movement/Blitting

    I'm coding a tile-based platform game and I'm having trouble getting the character movement working. I'm blitting everything in layers (background image, then level tiles, then player and enemies). Here's the document class I'm using:

    PHP Code:
    package app{

        
    import flash.display.Sprite;
        
    import flash.events.TimerEvent;
        
    import flash.utils.Timer;
        
    import app.loadData.MapData;
        
    import app.keyboard.CheckInput;
        
    import utils.Key;

        public class 
    Main extends Sprite {

            private var 
    mapData:MapData = new MapData;
            private var 
    checkInput:CheckInput = new CheckInput(stage);
            private var 
    key:Key = new Key(stage);
            private var 
    loaderTimer:Timer = new Timer(25);
            private var 
    gameTimer:Timer = new Timer(25);
            public static var 
    walkSpeed:Number = new Number(2);

            public function 
    Main():void {
                
    addChild(mapData.levelBitmap);
                
    mapData.currentLevel 2;
                
    mapData.loadMap();
                
    loaderTimer.addEventListener(TimerEvent.TIMERloadingComplete);
                
    loaderTimer.start();
            }
            private function 
    loadingComplete(e:TimerEvent):void {
                if (
    mapData.levelLoaded) {
                    
    loaderTimer.stop();
                    
    loaderTimer.removeEventListener(TimerEvent.TIMERloadingComplete);
                    
    gameTimer.addEventListener(TimerEvent.TIMERgameLoop);
                    
    gameTimer.start();
                }
            }
            private function 
    gameLoop(e:TimerEvent):void {
                
    checkInput.checkKeys();
                
    mapData.drawMap();
            }
        }

    And this is the MapData class that blits everything to the screen:

    PHP Code:
    package app.loadData{

        
    import flash.display.Sprite;
        
    import flash.display.Bitmap;
        
    import flash.display.BitmapData;
        
    import flash.geom.Rectangle;
        
    import flash.geom.Point;
        
    import flash.events.Event;
        
    import flash.net.URLLoader;
        
    import flash.net.URLRequest;

        public class 
    MapData extends Sprite {

            private var 
    _currentLevel:int = new int();
            private var 
    _levelLoaded:Boolean false;
            private var 
    _bitmapsLoaded:Boolean false;
            private var 
    _charPointX:int;
            private var 
    _charPointY:int;
            private var 
    levelLoader:URLLoader = new URLLoader();
            private var 
    levelXML:XML = new XML();

            private var 
    tileHeight:int40;
            private var 
    tileWidth:int40;
            private var 
    tileRows:int 11;
            private var 
    tileCols:int 15;
            private var 
    gameHeight:int 440;
            private var 
    gameWidth:int 600;
            private var 
    tileX:int 0;
            private var 
    tileY:int 0;

            private var 
    gr:GroundTileSheet = new GroundTileSheet(200,200);
            private var 
    st:StoneTileSheet = new StoneTileSheet(200,200);
            private var 
    charTest:CharTest = new CharTest(tileWidth,tileHeight);

            private var 
    level1BG:Level1BG = new Level1BG(880,440);
            private var 
    level2BG:Level1BG = new Level1BG(880,440);
            private var 
    level3BG:Level1BG = new Level1BG(880,440);
            
            private var 
    bgRect:Rectangle = new Rectangle(0,0,gameWidth,gameHeight);
            private var 
    bgPoint:Point = new Point(0,0);
            private var 
    tileRect:Rectangle = new Rectangle(0,0,tileWidth,tileHeight);
            private var 
    tilePoint:Point = new Point(0,0);
            private var 
    tileCanvasBD:BitmapData = new BitmapData(gameWidthgameHeighttrue);
            private var 
    canvasBD:BitmapData = new BitmapData(gameWidth,gameHeight,false,0x000000);
            private var 
    canvasBitmap:Bitmap = new Bitmap(canvasBD);

            public function 
    MapData():void {
            }
            public function 
    loadMap():void {
                
    loadXML();
            }
            public function 
    drawMap() {
                
    canvasBD.lock();
                if (!
    _bitmapsLoaded) {
                    
    loadBitmaps();
                }
                
    drawBG();
                
    drawLevel();
                
    drawChar();
                
    canvasBD.unlock();
            }
            private function 
    loadXML():void {
                if (!
    _levelLoaded) {
                    
    levelLoader.addEventListener(Event.COMPLETExmlDataLoaded);
                    
    levelLoader.load(new URLRequest("app/data/level"+_currentLevel+".xml"));
                }
            }
            private function 
    xmlDataLoaded(e:Event):void {
                
    levelXML XML(e.currentTarget.data);
                
    _levelLoaded true;        
            }
            private function 
    loadBitmaps():void {
                for (var 
    yCntr:int=0yCntr tileRowsyCntr++) {
                    for (var 
    xCntr:int=0xCntr tileColsxCntr++) {
                        
                        var 
    levelSplit:String levelXML.r[yCntr].split(",")[xCntr];
                        var 
    levelSheet:String levelSplit.split("-")[0];
                        var 
    levelCol:int levelSplit.split("-")[1];
                        var 
    levelRow:int levelSplit.split("-")[2];

                        
    tileRect.int(levelCol) * tileWidth;
                        
    tileRect.int(levelRow) * tileHeight;
                        
    tilePoint.xCntr tileWidth;
                        
    tilePoint.yCntr tileHeight;
                        
    tileCanvasBD.copyPixels(this[levelSheet], tileRecttilePoint);
                    }
                }
                
    _charPointX levelXML.s[0].split(",")[0]*tileWidth;
                
    _charPointY levelXML.s[0].split(",")[1]*tileHeight;
                
    _bitmapsLoaded true;
            }
            private function 
    drawBG():void {
                
    canvasBD.copyPixels(this["level"+_currentLevel+"BG"],bgRect,bgPoint);
            }
            private function 
    drawLevel():void {
                
    canvasBD.copyPixels(tileCanvasBDbgRectbgPoint);
            }
            private function 
    drawChar():void {
                var 
    charPoint:Point = new Point(_charPointX,_charPointY);
                var 
    charRect:Rectangle = new Rectangle(0,0,tileWidth,tileHeight);
                
    canvasBD.copyPixels(charTestcharRectcharPoint);
            }    
            public function 
    set currentLevel(level:int):void {
                
    _currentLevel level;
            }
            public function 
    get currentLevel():int {
                return 
    _currentLevel;
            }
            public function 
    get levelLoaded():Boolean {
                return 
    _levelLoaded;
            }
            public function 
    get bitmapsLoaded():Boolean {
                return 
    _bitmapsLoaded;
            }
            public function 
    get levelBitmap():Bitmap {
                return 
    canvasBitmap;
            }
            public function 
    set charPointX(pointX:int):void {
                
    _charPointX pointX;
            }
            public function 
    get charPointX():int {
                return 
    _charPointX;
            }
            public function 
    set charPointY(pointY:int):void {
                
    _charPointY pointY;
            }
            public function 
    get charPointY():int {
                return 
    _charPointY;
            }
        }

    Then this is my CheckInput class that checks for key presses:

    PHP Code:
    package app.keyboard{

        
    import flash.events.KeyboardEvent;
        
    import flash.display.DisplayObject;
        
    import flash.display.Stage;
        
    import flash.text.TextField;
        
    import flash.text.TextFormat;
        
    import app.Main;
        
    import app.loadData.MapData;
        
    import utils.Key;

        public class 
    CheckInput {
            private var 
    key:Key;
            private var 
    leftKey:int 37;
            private var 
    upKey:int 38;
            private var 
    rightKey:int 39;
            private var 
    downKey:int 40;
            private var 
    spaceBar:int 32;
            private var 
    keyState:TextField = new TextField();
            private var 
    jumpState:TextField = new TextField();
            private var 
    keyStateFormat:TextFormat = new TextFormat();
            private var 
    jumpStateFormat:TextFormat = new TextFormat();
            private var 
    mapD:MapData = new MapData();

            public function 
    CheckInput(stageVar:Stage) {
                
    key = new Key(stageVar);
                
    keyStateFormat.align jumpStateFormat.align "center";
                
    keyStateFormat.size jumpStateFormat.size 14;
                
    keyStateFormat.bold jumpStateFormat.bold true;
                
    keyState.width jumpState.width 200;
                
    keyState.height jumpState.height 30;
                
    keyState.jumpState.= (stageVar.stageWidth/2)-(keyState.width/2);
                
    keyState.= (stageVar.stageHeight/2)-10;
                
    jumpState.= (stageVar.stageHeight/2)+10;
                
                
    keyState.text "No Keys are Down";
                
    jumpState.text "Player is Not Jumping";
                
    keyState.setTextFormat(keyStateFormat);
                
    jumpState.setTextFormat(jumpStateFormat);
                
    stageVar.addChild(keyState);
                
    stageVar.addChild(jumpState);
            }
            
            public function 
    checkKeys() {
                if (
    key.isDown(leftKey)) {
                    
    keyState.text "Left Key is Down";
                    
    jumpState.text "Player is Not Jumping";
                    
    keyState.setTextFormat(keyStateFormat);
                    
    jumpState.setTextFormat(jumpStateFormat);
                    
    mapD.charPointX -= Main.walkSpeed;
                    
    trace(mapD.charPointX);
                } else if (
    key.isDown(upKey)) {
                    
    keyState.text "Up Key is Down";
                    
    jumpState.text "Player is Not Jumping";
                    
    keyState.setTextFormat(keyStateFormat);
                    
    jumpState.setTextFormat(jumpStateFormat);
                    
    mapD.charPointY-=Main.walkSpeed;
                    
    trace(mapD.charPointY);
                } else if (
    key.isDown(rightKey)) {
                    
    keyState.text "Right Key is Down";
                    
    jumpState.text "Player is Not Jumping";
                    
    keyState.setTextFormat(keyStateFormat);
                    
    jumpState.setTextFormat(jumpStateFormat);
                    
    mapD.charPointX+=Main.walkSpeed;
                    
    trace(mapD.charPointX);
                } else if (
    key.isDown(downKey)) {
                    
    keyState.text "Down Key is Down";
                    
    jumpState.text "Player is Not Jumping";
                    
    keyState.setTextFormat(keyStateFormat);
                    
    jumpState.setTextFormat(jumpStateFormat);
                    
    mapD.charPointY+=Main.walkSpeed;
                    
    trace(mapD.charPointY);
                } else if (
    key.isDown(spaceBar)) {
                    
    keyState.text "Space Bar is Down";
                    
    jumpState.text "Player is Jumping";
                    
    keyState.setTextFormat(keyStateFormat);
                    
    jumpState.setTextFormat(jumpStateFormat);
                } else {
                    
    keyState.text "No Keys are Down";
                    
    jumpState.text "Player is Not Jumping";
                    
    keyState.setTextFormat(keyStateFormat);
                    
    jumpState.setTextFormat(jumpStateFormat);
                }
            }
        }

    Right now the code is kind of a mess and but you should be able to get an idea how it works. The problem I'm having is that when the _charPointX and _charPointY variables are changed, it doesn't actually move charPoint to reflect the change, so my character doesn't move. The character is placed in the correct tile when the demo is loaded, meaning it's seeing the changes within the MapData class, but when I try to trace _charPointX and _charPointY from the CheckInput class, it gives me 0 and 0 instead of 0 and 6. I'm pretty new to OOP so I'm not sure what I'm doing wrong here - is there some easy solution that I'm just not seeing or am I going to have to completely rewrite everything?

  2. #2
    Pencil Farmer cadin's Avatar
    Join Date
    Jul 2006
    Location
    Vancouver BC
    Posts
    323
    You need to be using the same instance of MapData in Main and in CheckInput.
    Currently you create a new instance of that class inside each, which means when you change charPointX in the MapData that CheckInput has, it never touches the MapData that Main is checking. Does that make sense?

    A quick fix would be to pass the instance you create in Main to CheckInput:
    PHP Code:
    private var checkInput:CheckInput = new CheckInput(stagemapData); 
    Then in the CheckInput constructor just assign that instance of MapData to your mapD variable.

    You can probably figure out a better way to deal with that once you start cleaning things up, but that should get it working for now.

    Hope that helps.

  3. #3
    Member
    Join Date
    May 2008
    Location
    Ohio, USA
    Posts
    63
    Wow, I don't know how I missed that - I changed it so it passes mapData along to CheckInput and it works perfectly now. One more question - what's the difference between a public variable/function and a public static variable/function? As far as I can tell the only difference is it lets me reference variables in other classes without having to create an instance of the class that variable is in, but most of the time it just seems to break my code.

  4. #4
    Pencil Farmer cadin's Avatar
    Join Date
    Jul 2006
    Location
    Vancouver BC
    Posts
    323
    Yeah, you got it...
    Static means it's a property of the class itself rather than the object, so you can access it without having to instantiate the class.
    Static properties can be useful for holding constants for settings (eg. Hero.MAX_HEALTH).
    Static methods are usually things like utility methods (eg. Math.sqrt) that don't depend on instance variables to operate.

    If you're having problems it could be that you're trying to access instance variables from within a static method.
    Also remember that to refer to a static property you'd use the class name, rather than the instance name.

    Someone else here can probably give a more accurate explanation, but that's the jist of it.

    Glad you got it working!

  5. #5
    Member
    Join Date
    May 2008
    Location
    Ohio, USA
    Posts
    63
    Ok, so making a variable or function static completely changes the way you need to access it, which is probably why my code is breaking. My biggest problem is that I can't figure out how to access a public static variable from within the same class - I have to make them all private static variables and then use a getter if I want to access them from other classes. So far it really hasn't been a problem, though.

    One last question (I hope). I've got an XML file that stores data about my maps - which tiles to place where, what tile sheet they reside on, whether you can walk through them or not, etc. My movement isn't tile-based, though, and I'm having trouble getting it to work.

    I added a function to my MapData class called isWalkable. Here's the code for that:
    PHP Code:
    public function isWalkable(tileX:int,tileY:int):Boolean {
        var 
    isWalkableString:String levelXML.w[tileY].split(",")[tileX];
        var 
    isWalkable:Boolean false;
        if (
    isWalkableString == "true") {
            
    isWalkable true;
        } else {
            
    isWalkable false;
        }
        return 
    isWalkable

    For example, if I passed 0 and 1 to it then it would find whether the tile in row 0 column 1 is walkable, then return true if it is and false if it's not. I'm calling it from the checkKeys function of my CheckInput class like this:
    PHP Code:
    public function checkKeys():void {
        if (
    key.isDown(leftKey)) {
            if (
    mapD.isWalkable(Math.floor(mapD.charPointX/tileSize), Math.floor(mapD.charPointY/tileSize))) {
                
    mapD.charPointX-=walkSpeed;
            }
        } if (
    key.isDown(upKey)) {
            if (
    mapD.isWalkable(Math.floor(mapD.charPointX/tileSize), Math.floor(mapD.charPointY/tileSize))) {
                
    mapD.charPointY-=walkSpeed;
            }
        } if (
    key.isDown(rightKey)) {
            if (
    mapD.isWalkable(Math.floor((charX+tileSize)/tileSize), Math.floor(mapD.charPointY/tileSize))) {
                
    mapD.charPointX+=walkSpeed;
            }
        } if (
    key.isDown(downKey)) {
            if (
    mapD.isWalkable(Math.floor(mapD.charPointX/tileSize),  Math.floor((charY+tileSize)/tileSize))) {
                
    mapD.charPointY+=walkSpeed;
            }
        }

    Right now it's working perfectly for the right and bottom edges. When I walk to the left edge of the level, though, I get stuck until I move to the right again, and if I walk to the top edge I can actually move slightly past the top of the level, which gives me this error:
    PHP Code:
    TypeErrorError #1006: value is not a function.
    at app.loadData::MapData/isWalkable()
    at app.keyboard::CheckInput/checkKeys()
    ... 
    Obviously there's something wrong somewhere, but I can't figure out exactly what it is.

  6. #6
    Senior Member tonypa's Avatar
    Join Date
    Jul 2001
    Location
    Estonia
    Posts
    8,223
    You are currently detecting collision with point where character stands now, instead you should do collision check with point where character ends up after moving.

    Lets suppose character is at x=100 and there is block at the right side starting from x=101. Detecting collision with current point 100 says you can move right so you move 1 pixel right. Next time you try to move, your character is stuck inside the block because it will always detect collision at 101. So when you are still at 100 and attempt to move right by 1 pixel, you should detect collision with point 101, if there is block you cant move there and you remain at point 100 and you will not end up inside the block. Same way to move left you would detect collision with point 99 before moving the character.

  7. #7
    Member
    Join Date
    May 2008
    Location
    Ohio, USA
    Posts
    63
    Ok, here's the new code:
    PHP Code:
    public function isWalkable(tileX:int,tileY:int):Boolean {
        var 
    isWalkableString:String;
        var 
    isWalkable:Boolean false;
        if (
    tileX >= && tileY >= 0) {
            
    isWalkableString levelXML.w[tileY].split(",")[tileX];
        }
        if (
    isWalkableString == "true") {
            
    isWalkable true;
        }
        return 
    isWalkable

    PHP Code:
    public function checkKeys():void 
        if (
    key.isDown(leftKey)) { 
            if (
    mapD.isWalkable(Math.floor((mapD.charPointX-walkSpeed)/tileSize), Math.floor(mapD.charPointY/tileSize))) { 
            
    mapD.charPointX-=walkSpeed
            } 
        } if (
    key.isDown(upKey)) { 
            if (
    mapD.isWalkable(Math.floor(mapD.charPointX/tileSize), Math.floor((mapD.charPointY-walkSpeed)/tileSize))) { 
            
    mapD.charPointY-=walkSpeed
            }
        } if (
    key.isDown(rightKey)) { 
            if (
    mapD.isWalkable(Math.floor((charX+tileSize)/tileSize), Math.floor(mapD.charPointY/tileSize))) { 
                
    mapD.charPointX+=walkSpeed
            } 
        } if (
    key.isDown(downKey)) { 
            if (
    mapD.isWalkable(Math.floor(mapD.charPointX/tileSize),  Math.floor((charY+tileSize)/tileSize))) { 
                
    mapD.charPointY+=walkSpeed
            } 
        } 

    I figured out that the error when hitting the top edge was because it was testing for a negative Y value, which was causing it look for an entry in the XML file that doesn't exist. This presents me with a new problem, though. Eventually I'd like to have platforms, and the ability to scroll up, left, down or right. Right now I can't move past the top of the screen, though, since it'll cause charPointY to be negative. So what should I do? I was thinking I might create a container Sprite to hold everything, make it the size of the entire level, and then draw all my bitmaps to that. That way it'll test charPointX and charPointY against the container Sprite instead of the stage. I don't know how that would affect the performance of the game, though, and I'm sure there's a better way of doing it than that. Anyone have any ideas?

    EDIT: Never mind the above, I've got the scrolling working. I'm moving the level instead of the character once he gets near any of the edges (I have no idea why that never occurred to me before). Now I just have to add in items and enemies and get to work on some levels.
    Last edited by DoomOfTheLiving; 04-08-2009 at 03:31 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  




Click Here to Expand Forum to Full Width

HTML5 Development Center