Game Development Community

Free W-S-A-D movement code - for those who had problems with it

by Milan Rancic · in Torque Game Builder · 03/14/2008 (7:52 am) · 1 replies

I found that many people had problems with "simple" W-S-A-D movement in their games. So i decided to help and shere my code.
Here is one code for simple W-S-A-D movement that i use for my game. its completely bug-free.
- Don't ask me how it works not even i can figure it out now :P This code is (logically) not simple at all!
- in order this to work, you only have one (and only one) object with name "player"
- (for total beginners:) You can paste this code directly in your project/gameScripts/game.cs file,
or you can create a new *.cs file from this (in gameScripts folder), and then you exec-it from gameStart() function (game.cs file)

Engine properties:
- player movement is updated from onUpdate() - every 32Ms
- player-object is speeding up/slowing down by rate defined by players "ability" [inertia] stored IN the player itself (dynamic field). Also, player-object should have defined dynamic field [speed] that represent max linear velocity in ANY direction.
- When using this engine (as mentioned above) the player is moving @ the same rate in ANY direction (no faster moving by going diagonally)
- When you pres left+right or up+down @ the same time, the player is moving @ the direction of the last command pressed (he is not standing in place), after you release that "last command" the player continues to move at the direction of previous command (if still pressed)
- Optionally you can implement face-the-direction engine that rotates the [player] to face the direction where he is going to
- This code is tested and contains no known bug-issues

- if this was helpful, please be free to give me a feedback or some comment about code. Thnx in advance, and happy game developing!

(continued on next post - due to limitation of characters in one post)

#1
03/14/2008 (7:52 am)
///////////////////////////////////
//   Written by Milan Ranchich   //
//    EIPIX game development     //
//         www.eipix.com         //
// Contact: starcraft@sbb.co.yu  //
///////////////////////////////////


// set up //


// we can do this in [startGame()] function, or from [onLevelLoaded()]. If you already have onLevelLoaded function defined somewhere, than you should integrate this code in it, or you can define those dynamic fields directly in level builder for easy tweak-up.

// we store object [player] in global variable [$player] so we can change (eventually) who "the player" is, during the game.
function t2dscenegraph::onLevelLoaded(%this){
	$player = player; // store object (object's name) to global var, that will be treated as "player"
	$player.speed = 15; // modify this to change player's max speed [dynamic field]
	$player.inertia = 0.5; // modify this to change player's speedUp/speedDown speed [dynamic field]
	$player.target = "0 0"; // must have defined "target"
	
	$player.rotationSpeed = 150; // (Optional) Use this parameter only if you use the "face-the-direction" part of engine

	$player.enableUpdateCallback();
	setUpTheCommands();
}

// we call this from [startGame()] function, or from [onLevelLoaded()]
function setUpTheCommands(){
	moveMap.bindCmd(keyboard, "w", "press( 2);", "release( 2);");
	moveMap.bindCmd(keyboard, "s", "press( 7);", "release( 7);");
	moveMap.bindCmd(keyboard, "a", "press(13);", "release(13);");
	moveMap.bindCmd(keyboard, "d", "press( 3);", "release( 3);");
}


// engine //


function player::onUpdate(%this){
	playerMovement();
	
	$player.rotatePlayer(); // (optional) Use this if you want for player to rotate toward direction he is moving to
}

// this is called when you [press] some movement-key
function press(%command){
	$WSAD = $WSAD SPC %command;
	sectorization();
}

// this is called when you [release] some movement-key
function release(%command){
	$WSAD = strreplace($WSAD, " " @ %command, "");
	sectorization();
}

// here, we calculate the target-(8)direction where player should go, depending of last two pressed commands.
function sectorization(){
	%numberOfCmdsPressed = getWordCount($WSAD) - 1;
	%lastPressedCmd = getWord($WSAD,%numberOfCmdsPressed);
	%additionalCmd = getWord($WSAD,%numberOfCmdsPressed-1);
	%differenceFactor = %lastPressedCmd - %additionalCmd;
	%speed = $player.speed;
	%halfSpeed = $player.speed * ( mSqrt(2) / 2 );
	
	switch( mAbs(%differenceFactor) ){
		case 0:		$player.target = "0 0";
		case 1:		$player.target = %halfSpeed SPC -%halfSpeed;
		case 2:		$player.target = 0 SPC -%speed;
		case 3:		$player.target = %speed SPC 0;
		case 4:		$player.target = %halfSpeed SPC %halfSpeed;
		case 5:
					if(%differenceFactor < 0)
						$player.target = 0 SPC -%speed;
					else
						$player.target = 0 SPC %speed;
				
		case 6:		$player.target = -%halfSpeed SPC %halfSpeed;
		case 7:		$player.target = 0 SPC %speed;
		case 10:
					if(%numberOfCmdsPressed <= 2){
						if(%differenceFactor < 0)
							$player.target = %speed SPC 0;
						else
							$player.target = -%speed SPC 0;
					}
					else{
						%inverted = %additionalCmd SPC getWord($WSAD, %numberOfCmdsPressed - 2);
						$WSAD = strreplace($WSAD,  getWord($WSAD, %numberOfCmdsPressed - 2) SPC %additionalCmd, %inverted);
						sectorization();
					}
		case 11:	$player.target = -%halfSpeed SPC -%halfSpeed;
		case 13:	$player.target = -%speed SPC 0;
	}	
}	

// this is executing constantly from [onUpdate()] function (every 32Ms)
// this function is regulating movementSpeed/direction/speedUp/slowDown of the [$player] object
function playerMovement()
{
	%targetX = getWord($player.target,0);
	%targetY = getWord($player.target,1);
	%speedX = $player.getLinearVelocityX();
	%speedY = $player.getLinearVelocityY();
	%differenceFactorX = %targetX - %speedX;
	%differenceFactorY = %targetY - %speedY;
	
	if( mAbs(%differenceFactorX) > $player.inertia ){
		if(%differenceFactorX > 0)
			%speedX += $player.inertia;
		else
			%speedX -= $player.inertia;
	}
	else
		%speedX = %targetX;
		
	if( mAbs(%differenceFactorY) > $player.inertia ){
		if(%differenceFactorY > 0)
			%speedY += $player.inertia;
		else
			%speedY -= $player.inertia;
	}
	else
		%speedY = %targetY;
	
	$player.setLinearVelocity( %speedX, %speedY );
}



// optional "face-the-direction" part of engine //


// include this code if you want for player to face the direction he is moving to
function player::rotatePlayer(%player){
	%speed = getWord( %player.getLinearVelocityPolar(), 1 );
	%angle = getWord( %player.getLinearVelocityPolar(), 0 );
	%targetAngle = calculateAngle("0 0", %player.target);

	%difference = mAbs(%angle - %targetAngle);
	if(%difference > 180)
		%difference -= 180;
	
	if (%player.target !$= "0 0"){
		if (%difference > 140){
			%player.targetAngle = %targetAngle;
			%rotationSpeed = %player.rotationSpeed;
		}
		else {
			%player.targetAngle = %angle;
			%rotationSpeed = %player.rotationSpeed;
		}
	}
	else if( %speed > 5 ) {
		%player.targetAngle = %angle;
		%rotationSpeed = %player.rotationSpeed / 4;
		trejs.text = "";
	}
	else {
		%rotationSpeed = 1;
		trejs.text = "";
	}
	
	%player.rotateTo( %player.targetAngle, %rotationSpeed );
}

// this function marely calculates direction-angle between two points
function calculateAngle(%Y1, %Y2) 
{
	%X1 = getWord(%Y1,0);
	%Y1 = getWord(%Y1,1);

	%X2 = getWord(%Y2,0);
	%Y2 = getWord(%Y2,1);

	%X1 = %X2 - %X1;
	%Y1 = %Y1 - %Y2;
	
	%angle = mRadToDeg( mAtan( %X1, %Y1 ));

	return %angle;
}



// optional - correct collisions //


// if you are having trouble with colision (player walking trought objects) you can use this engine. this will forse [player] to stop (linearVelocity = 0) when colide with obstacle.

// when using this function, player's name or class must be "player". You can change this. Also you must enable collision physics/layers/groups on player-object.
function player::onCollision(%player, %dstObject, %srcRef, %dstRef, %time, %normal, %contacts, %points){
	pushBack(%player, %normal, 0.1);
}

// this function is pushing back objects that colide with other objects, so the cillision wont produce any walk-trough-wall bugs
function pushBack(%this, %normal, %pushDistance){
		%vectorX = getWord(%normal, 0);
		%vectorY = getWord(%normal, 1);
	
		if(%vectorX > 0.001){
			%this.setLinearVelocityX(0);
			%this.setPositionX(%this.getPositionX() + %pushDistance);
		}
		else if(%vectorX < -0.001){
			%this.setLinearVelocityX(0);
			%this.setPositionX(%this.getPositionX() - %pushDistance);
		}
			
		if(%vectorY > 0.001){
			%this.setLinearVelocityY(0);
			%this.setPositionY(%this.getPositionY() + %pushDistance);
		}
		else if(%vectorY < -0.001){
			%this.setLinearVelocityY(0);
			%this.setPositionY(%this.getPositionY() - %pushDistance);
		}
}