Avoiding blocking keys and mouse
by MrSpaceGame · in Torque Game Builder · 05/02/2012 (8:11 am) · 4 replies
Hi Guys.
I am currently learning TileMaps and I am fiddling with Collision and movement in general.
I ran into an isse where I hoped you could help me out.
I have a tilemap where the player can run around, with some obstacles.
This is my collision function, some code is from the Pacman Example in the TDN:
This is the movement code:
It actually works that the player is blocked, but when an obstacle is passed, I have to press WASD again to move into that direction again. Any good ideas how to avoid that? I could just add velocity when I set the blockers to false, but how do I query if the key is still pressed?
My second question is similar.
I let the player shoot with the mouse.
That's how do it:
Okay, pretty easy, but when I press the mouse button the crosshair sticks to the position where it is pressed. Any advice on that problem?
Thanks people! :)
I am currently learning TileMaps and I am fiddling with Collision and movement in general.
I ran into an isse where I hoped you could help me out.
I have a tilemap where the player can run around, with some obstacles.
This is my collision function, some code is from the Pacman Example in the TDN:
function ScriptPlayer::checkMovementCollision(%this)
{
// Pick Current Tile
%currentTile = Layer.pickTile(%this.getPosition());
// Get Coords
%x = getWord(%currentTile, 0);
%y = getWord(%currentTile, 1);
// Get next tiles
%leftTile = Layer.getTileCustomData((%x - 1) SPC %y);
%rightTile = Layer.getTileCustomData((%x + 1) SPC %y);
%upTile = Layer.getTileCustomData(%x SPC (%y - 1));
%downTile = Layer.getTileCustomData(%x SPC (%y + 1));
// Check Left Side
if ( %leftTile $= "WALL" && %this.getLinearVelocityX() < 0 )
{
// Block Movement
%this.blockLeft = true;
// Stop Player
if ( %this.getLinearVelocityX() < 0 )
{
%this.setLinearVelocityX(0);
}
}
else
{
%this.blockLeft = false;
}
// Check Right Side
if ( %rightTile $= "WALL" && %this.getLinearVelocityX() > 0 )
{
%this.blockRight = true;
if ( %this.getLinearVelocityX() > 0 )
{
%this.setLinearVelocityX(0);
}
}
else
{
%this.blockRight = false;
}
// Check upwards
if ( %upTile $= "WALL" )
{
%this.blockUp = true;
if ( %this.getLinearVelocityY() < 0 )
{
%this.setLinearVelocityY(0);
}
}
else
{
%this.blockUp = false;
}
// Check downwards
if ( %downTile $= "WALL" )
{
%this.blockDown = true;
if ( %this.getLinearVelocityY() > 0 )
{
%this.setLinearVelocityY(0);
}
}
else
{
%this.blockDown = false;
}
}This is the movement code:
function playerUp()
{
if ($Player.blockUp == false ){
$Player.setLinearVelocityY( -$Player.vSpeed );
}
}
function playerDown()
{
if ( $Player.blockDown == false ){
$Player.setLinearVelocityY( $Player.vSpeed );
}
}
function playerLeft()
{
if ( $Player.blockLeft == false ){
$Player.setLinearVelocityX( -$Player.hSpeed );
}
}
function playerRight()
{
if ( $Player.blockRight == false ){
$Player.setLinearVelocityX( $Player.hSpeed );
}
}It actually works that the player is blocked, but when an obstacle is passed, I have to press WASD again to move into that direction again. Any good ideas how to avoid that? I could just add velocity when I set the blockers to false, but how do I query if the key is still pressed?
My second question is similar.
I let the player shoot with the mouse.
That's how do it:
function sceneWindow2D::onMouseDown( %this, %modifier, %worldPos, %mouseClicks )
{
if ( $ingame == true )
{
$Player.fire();
}
}Okay, pretty easy, but when I press the mouse button the crosshair sticks to the position where it is pressed. Any advice on that problem?
Thanks people! :)
About the author
#2
When I walk to the left and approach a Tile that has collision and "WALL" as custom data, I need to block movement of course.
The above code works so far, but when the obstace is passed, I need to press "Left" or "A" again to make my playersprite walk to the left again.
Also, the code does not work as well as I hoped. The distance (when it starts blocking) between the Playersprite and the Wall varies a bit.
The calculation takes place in sceneWindow2D::onUpdateScene() and ScriptPlayer::onCollision().
Here is the complete player.cs:
http://pastebin.com/WBkD9DJ4
(It is set to unlisted so only readers of this forums can read it)
Here is a Build to try it out:
http://www5.zippyshare.com/v/55433967/file.html
Please note that this game is only to learn the engine, the art is from Torque2D's Sample and the Fox from opengameart.com.
05/03/2012 (3:55 am)
Sorry, I try again.When I walk to the left and approach a Tile that has collision and "WALL" as custom data, I need to block movement of course.
The above code works so far, but when the obstace is passed, I need to press "Left" or "A" again to make my playersprite walk to the left again.
Also, the code does not work as well as I hoped. The distance (when it starts blocking) between the Playersprite and the Wall varies a bit.
The calculation takes place in sceneWindow2D::onUpdateScene() and ScriptPlayer::onCollision().
Here is the complete player.cs:
http://pastebin.com/WBkD9DJ4
(It is set to unlisted so only readers of this forums can read it)
Here is a Build to try it out:
http://www5.zippyshare.com/v/55433967/file.html
Please note that this game is only to learn the engine, the art is from Torque2D's Sample and the Fox from opengameart.com.
#3
Now let's look at that checkCollisionMovement function. Right now it gets called on every frame as well as every collision -- ideally we would only want it to be called whenever there is a collision. However to know you are no longer blocked by a wall you have to continually check it, so how do we get around that? Well there are (at least) two ways. The easy way is to take advantage of Torques physics engine and set your players collision response mode to "CLAMP," which prevents it from entering objects it collides with (given it has "send collision" and "send physics" checked). Then you will no longer have to do collision logic and should get perfectly consistent behavior.
However if you really want to handle the logic yourself, the other way is to make the checkMovementCollision function recursive; store a variable that's true whenever it detects a collision, and have it call itself at the end using the "schedule" method while that variable is true. Then it will begin checking for collisions whenever it collides, and stop checking when it no longer detects any. You'll also want it to not call "checkMovementCollision" if it already is checking, to prevent runaway concurrent calls. To make it more accurate, I would base the coordinates on the players position rather than the tile they are in. The only way I can think of doing this at the moment is to have it pickTiles for each neighboring tile using the players position (plus/minus half its width, and half its height, to get its edge's position). Slightly less efficient, but then again you won't be calling it every frame anymore.
Now for the movement, I am guessing you are using behaviors. Well if memory serves me correctly, you can have behavior key presses do multiple things, so if we wanted it to continually try to move the player down while the 's' key was pressed, we would do something similar to the recursive method above. On key down, it would set a variable like "downKey" to true then call "playerDown." Then playerDown would continue calling itself at the end by using "schedule" while that variable was true. Finally on key up we would want it to set the variable to false and break the cycle.
As for the cursor, I believe that is a problem with the behavior. You might need to bind movement to "onMouseDragged" or something to that effect to get it to behave. Otherwise, you could also just change the mouses position manually in "function sceneWindow2D::onMouseMove( %this, %modifier, %worldPos, %mouseClicks )."
Phew, so that's that. I hope that helps, if you have questions, please let me know.
05/03/2012 (12:54 pm)
Ah okay now I see. Okay I have a few tips to help you out. First and foremost, whenever you want something continuously updating, you should tie it to the onSceneUpdateTick callback instead of onUpdateScene. The reason for this is that onUpdateScene gets called on every single frame and can vary depending on performance, while onSceneUpdateTick gets called consistently around every 32 milliseconds, which is also about where the physics operate. Doing this gives you more consistent behavior and saves processing time to boot.Now let's look at that checkCollisionMovement function. Right now it gets called on every frame as well as every collision -- ideally we would only want it to be called whenever there is a collision. However to know you are no longer blocked by a wall you have to continually check it, so how do we get around that? Well there are (at least) two ways. The easy way is to take advantage of Torques physics engine and set your players collision response mode to "CLAMP," which prevents it from entering objects it collides with (given it has "send collision" and "send physics" checked). Then you will no longer have to do collision logic and should get perfectly consistent behavior.
However if you really want to handle the logic yourself, the other way is to make the checkMovementCollision function recursive; store a variable that's true whenever it detects a collision, and have it call itself at the end using the "schedule" method while that variable is true. Then it will begin checking for collisions whenever it collides, and stop checking when it no longer detects any. You'll also want it to not call "checkMovementCollision" if it already is checking, to prevent runaway concurrent calls. To make it more accurate, I would base the coordinates on the players position rather than the tile they are in. The only way I can think of doing this at the moment is to have it pickTiles for each neighboring tile using the players position (plus/minus half its width, and half its height, to get its edge's position). Slightly less efficient, but then again you won't be calling it every frame anymore.
Now for the movement, I am guessing you are using behaviors. Well if memory serves me correctly, you can have behavior key presses do multiple things, so if we wanted it to continually try to move the player down while the 's' key was pressed, we would do something similar to the recursive method above. On key down, it would set a variable like "downKey" to true then call "playerDown." Then playerDown would continue calling itself at the end by using "schedule" while that variable was true. Finally on key up we would want it to set the variable to false and break the cycle.
As for the cursor, I believe that is a problem with the behavior. You might need to bind movement to "onMouseDragged" or something to that effect to get it to behave. Otherwise, you could also just change the mouses position manually in "function sceneWindow2D::onMouseMove( %this, %modifier, %worldPos, %mouseClicks )."
Phew, so that's that. I hope that helps, if you have questions, please let me know.
#4
I forgot to set this in the Datablock of my player. I spawn him manually and use the editor only as some sort of TileEditor.
And then I thought I need to it manually for Tilemaps.. -.-^^
Thanks for everything you have written, I'll try to improve the manual collision detection :D.
And sorry for the late answer, I was busy modding Skyrim hehe.
edit:
Here is the code I came up with for the non blocking movement.
Works like a charm :).
Solution for the mouse:
I use it as cursor now, not as t2dStaticSprite that gets moved:
05/05/2012 (10:17 am)
Quote:Dang. I was so dumb.
The easy way is to take advantage of Torques physics engine and set your players collision response mode to "CLAMP,"
I forgot to set this in the Datablock of my player. I spawn him manually and use the editor only as some sort of TileEditor.
And then I thought I need to it manually for Tilemaps.. -.-^^
Thanks for everything you have written, I'll try to improve the manual collision detection :D.
And sorry for the late answer, I was busy modding Skyrim hehe.
edit:
Here is the code I came up with for the non blocking movement.
Works like a charm :).
function playerUp()
{
$Player.setLinearVelocityY( -$Player.vSpeed );
$playerUpEvent = schedule( 50, 0, "playerUp");
}
function playerDown()
{
$Player.setLinearVelocityY( $Player.vSpeed );
$playerDownEvent = schedule( 50, 0, "playerDown" );
}
function playerLeft()
{
$Player.setLinearVelocityX( -$Player.hSpeed );
$playerLeftEvent = schedule( 50, 0, "playerLeft" );
}
function playerRight()
{
$Player.setLinearVelocityX( $Player.hSpeed );
$playerRightEvent = schedule( 50, 0, "playerRight" );
}
function playerUpStop()
{
$Player.setLinearVelocityY( 0 );
if ( isEventPending( $playerUpEvent ) ){
cancel( $playerUpEvent );
}
}
function playerDownStop()
{
$Player.setLinearVelocityY( 0 );
if ( isEventPending( $playerDownEvent ) ){
cancel( $playerDownEvent );
}
}
function playerLeftStop()
{
$Player.setLinearVelocityX( 0 );
if ( isEventPending( $playerLeftEvent ) ){
cancel( $playerLeftEvent );
}
}
function playerRightStop()
{
$Player.setLinearVelocityX( 0 );
if ( isEventPending( $playerRightEvent ) ){
cancel( $playerRightEvent );
}
}Solution for the mouse:
I use it as cursor now, not as t2dStaticSprite that gets moved:
new GuiCursor(CrosshairCursor)
{
bitmapName = "~/data/images/crosshair.png";
};
Canvas.setCursor(CrosshairCursor);
Torque Owner Justin Proffitt