Movement snapped to a grid


(Nirvan) #2

You can simply try to do something like:

x = Math.round( x / gridSize ) * gridSize;


(Kevin Graham) #3

Sorry I guess the titles a bit misleading, I want it to only stop moving if its reached the next square, I don’t want tit to jump quickly.


(Kevin Graham) #4

I just want the maze movement to be smoother.


(Kevin Graham) #5

Maybe something with tweens? IDK how one would do that though.


(Jacob Albano) #6

You could definitely use tweens for this.



if (INPUT_RIGHT) // do your own check here
{
    var tween:VarTween = new VarTween(null, ONESHOT);
    tween.tween(this, "x", x + gridSize, 0.5);
    addTween(tween, true);
}

That should get you started on the right track; it should be pretty easy to adapt it for the other directions. You’ll want to make sure to only start the tween if you aren’t currently moving.


(Kevin Graham) #7

Thanks so much! I’ll try this out when I get home. I really appreciate the help! :smile:


(Abel Toy) #8

The thing with tweens is maybe you could get conflicts if moving too fast, or if you want to keep the key pressed to keep moving, without noticing stops at each tile.

What I’d do is have a vx and vy variables (which stand for velocity x and y, you can name them whatever you want). The movement should check input for each key and change these variables as such. This will make your game have constant movement.

Now we want the player to stop when the movement keys are no longer being pressed. To achieve that, we want to check if the x and y positions are rounded to a tile. The problem is, with a variable frameRate, maybe we’re going from 31 to 33, therefore it would never be at 32, and so it would never stop.

To solve this little issue, we need to store where is the player when the keys are no longer being pressed, and as soon as that stored position changes, we stop the player, and snap it to the exact position.

You might want to tweak the behaviour to fit your exact needs and the feeling you want for your movement, but that’s more or less what I’d start with. If you have any doubts or need any code, please let me know.


(Kevin Graham) #9

Thanks so much, i’ll give both methods a try, and see what works best! The tweens seems simpler, so if I can get it doing what I want ill probs use that.


(Zachary Lewis) #10

I liked this problem, so I made a little implementation. It should lock your movement to the grid.


Play around with the demo. It’s fun!

Move with the arrows and generate a new world with R.


I put it all into a single world (see it in the Gist below), so you’ll probably want to extract some of the functionality into a specialized Entity.


(Abel Toy) #11

What about keeping the movement keys pressed? :stuck_out_tongue:

Also, if you go crazy with the keys (pressing two at once to go diagonal) you can get through walls


(Zachary Lewis) #12

If you wanted to continue to move when a key is held, I’d add a callback when _tween completes. This would check for input and, if present, add a new point to _tween.

I did some tests and, although you will tween through walls, it is impossible to reach an unreachable location by mashing buttons. _playerLocation is the true location of the player, and it should never be an invalid location.

Keep in mind that this was something I put together in an hour and should be used as a proof-of-concept and not production code.


(Kevin Graham) #13

In my game I check if they would collide in the space they are trying to move to before I move them ,and only do if they aren’t going to collide: `

	override public function update():void
	{
		if (x % 40 == 0)
		{
			if (Input.check(Key.LEFT))
			{
				if (!colliding(new Point(x - 40, y)))
				{
					if (acceptInput)
					{
						var tween:VarTween = new VarTween(null, ONESHOT);
						tween.tween(this, "x", x - 40, 0.5);
						addTween(tween, true);
						sprKoala.play("left")
					}
				}
			}
			
			else if (Input.check(Key.RIGHT))
			{
				if (!colliding(new Point(x + 40, y)))
				{
					if (acceptInput)
					{
						var tween:VarTween = new VarTween(null, ONESHOT);
						tween.tween(this, "x", x + 40, 0.5);
						addTween(tween, true);
						sprKoala.play("right")
					}
				}
			}
			
			
			else if (Input.check(Key.UP))
			{
				if (!colliding(new Point(x, y - 40)))
				{
					if (acceptInput)
					{
						var tween:VarTween = new VarTween(null, ONESHOT);
						tween.tween(this, "y", y - 40, 0.5);
						addTween(tween, true);
						sprKoala.play("up")
					}
				}
			}
			
			else if (Input.check(Key.DOWN))
			{
				if (!colliding(new Point(x, y + 40)))
				{
					if (acceptInput)
					{
						var tween:VarTween = new VarTween(null, ONESHOT);
						tween.tween(this, "y", y + 40, 0.5);
						addTween(tween, true);
						sprKoala.play("down")
					}
				}
			}
			else
			{
				//add no movement code here
			}
		}
		
		
	 //***********************************************************//
		super.update();
	}
	
	public function colliding(position:Point):Boolean // A function for checking if there is a wall in front of the player
	{
		if (collide("solid", position.x, position.y)) return true;
		else if (collide("koala", position.x, position.y)) return true;
		else return false;
	}`

(Kevin Graham) #14

It’s doing what I want for the most part, except now when the tweens done I want to snap it perfectly to the grid, because its not quite staying on it.


(Abel Toy) #15

You can use the tween’s completed function for that. When you create the function, instead of passing null as the first parameter, pass something like onTweenCompleted or whatever you want to name it. Then, you create that function, and in the function you snap the player perfectly to the grid.

EDIT: Although… this should be handled by the tweens themselves, I think… gonna file an Issue.


Tweening appears to be working properly
(Kevin Graham) #16

Thank you, that helped a lot. The movement works perfectly for left and right, but for up and down you can see it jumping, and for up it snaps to the next square above. I’ll post my code and a demo in a sec.


(Kevin Graham) #17

Alright, the demo is here, please note I did not design the game nor make the graphics, its a tutorial I did for gamemaker a long time ago that I am redoing in FP for practice. One last thing, the object’s X and Y is printed to the console for debugging purposes. Any help as to why it works on the horizontal but not vertical is greatly appreciated.

Also, here is the code for the player object:

package Entitys
{
	import flash.geom.Point;
	import net.flashpunk.Entity;
	import net.flashpunk.graphics.Image;
	import net.flashpunk.graphics.Spritemap;
	import net.flashpunk.utils.Input;
	import net.flashpunk.utils.Key;
	import net.flashpunk.FP;
	import net.flashpunk.tweens.misc.VarTween;

	public class Player extends Entity
	{
		[Embed(source = "../assets/Koala.png")] private const KOALA:Class; // Koalas sprite map
		[Embed(source = "../assets/KoalaDead.png")] private const KOALADEAD:Class; // Dead koala sprite
		[Embed(source = "../assets/GUI/Rescued.png")] private const RESCOUNT:Class; //The GUI lable for rescued koalas
		public var sprKoala:Spritemap = new Spritemap(KOALA, 40, 40);
		
		private var isDead:Boolean = new Boolean(false);
		private var DeadFirst:Boolean = new Boolean(false);
		private var acceptInput:Boolean = new Boolean(true);
		private var dieX:int = new int(0);
		private var dieY:int = new int(0);
		private var dir:int = new int(3);//clockwise from 1, the direction the object in facing
		private var isStopped:Boolean = new Boolean(true);
		private var tween:VarTween = new VarTween(tweenEnd, ONESHOT);
		
		public function Player()
		{
			setHitbox(40, 40);
			
			type = "koala";
			
			addTween(tween, false);
			
		 //************************* Animations **********************//
			graphic = sprKoala; // Set the koalas sprite to be the animating sprite map
			
			sprKoala.add("left", [4, 5, 6, 7], 7, true);
			sprKoala.add("right", [8, 9, 10, 11], 7, true);
			sprKoala.add("up", [12,13,14,15], 7, true);
			sprKoala.add("down", [0, 1, 2, 3], 7, true);
		 //**********************************************************//
		}
		
		override public function update():void
		{
			FP.console.log("X=" + x + ", Y=" + y);
		 //************************* Controls ***********************//
			var speed:int = new int;
			speed = 4; // player speed
			
			if (isDead) //TODO: Fix death code
			{
				DeadFirst = true;
				isDead = false;
				acceptInput = false;
				graphic = new Image(KOALADEAD);
				dieX = x;
				dieY = y;
			}
			
			if (DeadFirst)
			{
				x += 2;
				y = (((x - dieX) * (x - dieX)) + 75) + dieY;
				FP.console.log("Info: " + y);
			}
			
			if (x % 40 == 0)
			{
				if (Input.check(Key.LEFT))
				{
					if (!colliding(new Point(x - 40, y)))
					{
						if (acceptInput)
						{
							tween.tween(this, "x", x - 40, 0.5);
							tween.start();
							sprKoala.play("left")
							dir = 4;
							isStopped = false;
						}
					}
				}
				
				else if (Input.check(Key.RIGHT))
				{
					if (!colliding(new Point(x + 40, y)))
					{
						if (acceptInput)
						{
							tween.tween(this, "x", x + 40, 0.5);
							tween.start();
							sprKoala.play("right")
							dir = 2;
							isStopped = false;
						}
					}
				}
				
				
				else if (Input.check(Key.UP))
				{
					if (!colliding(new Point(x, y - 40)))
					{
						if (acceptInput)
						{
							tween.tween(this, "y", y - 40, 0.5);
							tween.start();
							sprKoala.play("up")
							dir = 1;
							isStopped = false;
						}
					}
				}
				
				else if (Input.check(Key.DOWN))
				{
					if (!colliding(new Point(x, y + 40)))
					{
						if (acceptInput)
						{
							tween.tween(this, "y", y + 40, 0.5);
							tween.start();
							sprKoala.play("down")
							dir = 3;
							isStopped = false;
						}
					}
				}
				else //No movement keys are being pressed
				{
					if (isStopped)
					{
						if (dir == 1)//up
						{
							sprKoala.setFrame(1, 3);
						}
						else if (dir == 2)//right
						{
							sprKoala.setFrame(1, 2);
						}
						else if (dir == 3)//down
						{
							sprKoala.setFrame(1, 0);
						}
						else if (dir == 4)//left
						{
							sprKoala.setFrame(1, 1);
						}
					}
				}
			}
			
			if (!DeadFirst)
			{
				if (collide("instakill", x, y))
				{
					isDead = true;
				}
			}
		 //***********************************************************//
			super.update();
		}
		
		private  function tweenEnd():void
		{
			x -= x % 40;//TODO: Fix this code so it doesnt jump around when moving up n stuff
			y -= y % 40;
			isStopped = true;
		}
		
		private function colliding(position:Point):Boolean // A function for checking if there is a wall in front of the player
		{
			if (collide("solid", position.x, position.y)) return true;
			else if (collide("koala", position.x, position.y)) return true;
			else return false;
		}
		
	}
}

(Kevin Graham) #18

Any thoughts on why this is happening?


(Kevin Graham) #19

Anyone? I think it might be an issue with the tween code or something because I use the same code for horizontal movement as vertical.


(Zachary Lewis) #20

It looks like when it reaches the end of an upward tween, it checks to see what square it should be in and is floored to the lower one (probably due to the % operator use).

I’d try to rewrite your code using a logic flow like this:

  1. A movement key was pressed.
  2. Can the player move to the adjacent tile based on the input?
  3. If so, determine where the player should end up.
  4. Start the tween using the determined location.
  5. Once the tween is completed, jump to step 1.

(Kevin Graham) #21

Yea that’s what I did at the start, but it wasn’t working properly, the tween was moving it too far. Thats why I had to make it snap to the grid like that to make up for it.