Hello! I am currently in the prossess of making a maze game, and I want to have the objects snap to a grid when they move. So when you press the button, it will move one whole square, at a minimum. If you hold it down it will keep going, and only stop when the button is released AND it is snapped to the grid. Both the graphics and the grid are 40 px x 40 px. Thanks for your time!
Movement snapped to a grid
You can simply try to do something like:
x = Math.round( x / gridSize ) * gridSize;
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.
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.
Thanks so much! I’ll try this out when I get home. I really appreciate the help!
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.
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.
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
.
What about keeping the movement keys pressed?
Also, if you go crazy with the keys (pressing two at once to go diagonal) you can get through walls
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.
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;
}`
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.
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
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.
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;
}
}
}
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.
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:
- A movement key was pressed.
- Can the player move to the adjacent tile based on the input?
- If so, determine where the player should end up.
- Start the tween using the determined location.
- Once the tween is completed, jump to step 1.