Top-Down 8-Directional Shooter: Das Swarmy, Part 2
by Bryan Sawler · 02/26/2011 (5:19 pm) · 2 comments
Coming back to this I saw a few problems
1) The controls suck
2) Crashes constantly after X enemies spawn (on mCurrentPrimitiveBuffer->prepare())
3) The room was too boring
So for this part, we'll take a moment to address these 3 issues
Step 1: The controls.
Now we've been using a mouse this whole time which is less than great for a top-down game. So we're going to go back into game/scripts/client/default.bind.cs and change a few things. Find function moveleft(%val) and above that we're going to add:
What this is going to do is allow us 8-directional dpad-style movement with up/down/left/right and w/a/s/d keys. This could be a lot easier with source changes but I decided this will be all script, so all script it is. We store the current rotation in $currentRot because the $mvYaw we set is a delta, NOT the total rotation of the player. So whenever we want to switch directions we have to do it based on the current rotation.
We also replace moveleft, moveright, moveforward and movebackward with:
These simply set the flags used by updateMovement to set the 8-directional movement and velocity.
While we're at it find the mouse's xaxis line, and comment it out so that
moveMap.bind( mouse, xaxis, yaw );
becomes
//moveMap.bind( mouse, xaxis, yaw );
We may also want to remap shooting to another key instead of mouse click. To do this find
moveMap.bind( mouse, button0, mouseFire );
and below that add
moveMap.bind( keyboard, m, mouseFire);
Which will make the m key ALSO shoot.
Save, remove prefs.cs and config.cs and voila, more proper controls!
Step 2: The crash.
For this, I could find no script-fix. To be honest I couldn't even find a good code fix. So instead, I went a "fix". In basicSceneObjectLightingPlugin.cpp, in renderShadow comment out both lines that reference mShadow, and above that in updateShadow, comment out everything in that function. No more shadows, but also no more crashes.
Will revisit this later and/or hope it's fixed in 1.1
If you don't have source access, and are stucco with basic lighting/on a mac, well I'm sorry I guess?
And Step 3: The room
So I went in and added a few more instances of the cube for pillars. Updated map:

Along with that, I've been tweaking the enemy swarming a bit
First we ditch the 30-degree test and only check at 60 and 90.
Also we test 0.5 units out instead of 1 full unit. Lastly we set a move target 5 units out (so you'll see the vector scaled by 10, since it's a half-unit vector) and only update the check every second instead of twice per second.
Step 4. Some SwarmPlayer tweaks
A few more adjustments to the datablock in art/datablocks/aiPlayer.cs:
1) The controls suck
2) Crashes constantly after X enemies spawn (on mCurrentPrimitiveBuffer->prepare())
3) The room was too boring
So for this part, we'll take a moment to address these 3 issues
Step 1: The controls.
Now we've been using a mouse this whole time which is less than great for a top-down game. So we're going to go back into game/scripts/client/default.bind.cs and change a few things. Find function moveleft(%val) and above that we're going to add:
// We're currently at 0 (facing up)
$currentRot = 0;
$leftPressed = 0;
$rightPressed = 0;
$upPressed = 0;
$downPressed = 0;
function setSpeed(%speed)
{
if(%speed)
$movementSpeed = %speed;
}
function rotateTo(%target)
{
echo("currentRot was " @ $currentRot @ " and now is " @ %target);
%delta = %target-$currentRot;
if(%delta > mDegToRad(180))
{
%delta = %delta-mDegToRad(360);
}
if(%delta < -mDegtoRad(180))
{
%delta += mDegToRad(360);
}
$mvYaw += %delta;
$currentRot = %target;
}
function updateMovement()
{
%target = -1;
if($upPressed > 0)
{
if($leftPressed > 0) %target = -mDegToRad(45);
else if($rightPressed > 0) %target = mDegToRad(45);
else %target = 0;
}
else if($downPressed > 0)
{
if($leftPressed > 0) %target = mDegToRad(180)+mDegToRad(45);
else if($rightPressed > 0) %target = mDegToRad(180)-mDegToRad(45);
else %target = mDegToRad(180);
}
else if($leftPressed > 0)
{
%target = -mDegToRad(90);
}
else if($rightPressed > 0)
{
%target = mDegToRad(90);
}
if(%target != -1)
{
rotateTo(%target);
$mvForwardAction = $movementSpeed;
}
else
{
$mvForwardAction = 0;
}
}What this is going to do is allow us 8-directional dpad-style movement with up/down/left/right and w/a/s/d keys. This could be a lot easier with source changes but I decided this will be all script, so all script it is. We store the current rotation in $currentRot because the $mvYaw we set is a delta, NOT the total rotation of the player. So whenever we want to switch directions we have to do it based on the current rotation.
We also replace moveleft, moveright, moveforward and movebackward with:
function moveleft(%val)
{
$leftPressed = %val;
updateMovement();
}
function moveright(%val)
{
$rightPressed = %val;
updateMovement();
}
function moveforward(%val)
{
$upPressed = %val;
updateMovement();
}
function movebackward(%val)
{
$downPressed = %val;
updateMovement();
}These simply set the flags used by updateMovement to set the 8-directional movement and velocity.
While we're at it find the mouse's xaxis line, and comment it out so that
moveMap.bind( mouse, xaxis, yaw );
becomes
//moveMap.bind( mouse, xaxis, yaw );
We may also want to remap shooting to another key instead of mouse click. To do this find
moveMap.bind( mouse, button0, mouseFire );
and below that add
moveMap.bind( keyboard, m, mouseFire);
Which will make the m key ALSO shoot.
Save, remove prefs.cs and config.cs and voila, more proper controls!
Step 2: The crash.
For this, I could find no script-fix. To be honest I couldn't even find a good code fix. So instead, I went a "fix". In basicSceneObjectLightingPlugin.cpp, in renderShadow comment out both lines that reference mShadow, and above that in updateShadow, comment out everything in that function. No more shadows, but also no more crashes.
Will revisit this later and/or hope it's fixed in 1.1
If you don't have source access, and are stucco with basic lighting/on a mac, well I'm sorry I guess?
And Step 3: The room
So I went in and added a few more instances of the cube for pillars. Updated map:

Along with that, I've been tweaking the enemy swarming a bit
First we ditch the 30-degree test and only check at 60 and 90.
Also we test 0.5 units out instead of 1 full unit. Lastly we set a move target 5 units out (so you'll see the vector scaled by 10, since it's a half-unit vector) and only update the check every second instead of twice per second.
function AIPlayer::checkMovement(%this)
{
%mask = -1;
%index = %this.getNearestPlayerTarget();
if(%index == -1)
{
%this.schedule(2000, "checkMovement");
return;
}
%client = ClientGroup.getObject(%index);
%target = %client.player;
%playerPos = %target.getPosition();
%pos = %this.getPosition();
// Get the vector to the player
%vec = VectorSub(%playerPos, %pos);
%vec = VectorNormalize(%vec);
%vec = VectorScale(%vec, 0.5);
// Move our position 1 unit up in the air so we're not dragging on the ground
%pos = VectorAdd(%pos, "0 0 1");
%raytest = containerRayCast(%pos, VectorAdd(%pos, %vec), %mask, %this);
if(%raytest $= "0")
{
// We didn't hit anything so we can move forward! Moving in 2-unit scales so we don't constantly start/stop
%this.setMoveDestination(VectorAdd(%pos, VectorScale(%vec, 10)));
}
else
{
// We hit something - IF it's our target, deal damage, otherwise try to move around the obstacle
if(getWord(%raytest, 0) == %target)
{
%target.damage(%this, %targetPos, 1, "melee");
%this.schedule(1000, "checkMovement");
return;
}
%angle = mAtan(getWord(%vec, 1), getWord(%vec, 0));
for(%offset = 60; %offset <= 90; %offset += 30)
{
%testangle = %angle + mDegToRad(offset);
%newVec = mCos(%testangle) SPC mSin(%testangle) @ " 0";
%newVec = VectorScale(%newVec, 0.5);
%raytest = containerRayCast(%pos, VectorAdd(%pos, %newVec), %mask, %this);
if(%raytest $= "0")
{
// We didn't hit anything so we can move forward! Moving in 2-unit scales so we don't constantly start/stop
%this.setMoveDestination(VectorAdd(%pos, VectorScale(%newVec, 10)));
}
%testangle = %angle - mDegToRad(offset);
%newVec = mCos(%testangle) SPC mSin(%testangle) @ " 0";
%newVec = VectorScale(%newVec, 0.5);
%raytest = containerRayCast(%pos, VectorAdd(%pos, %newVec), %mask, %this);
if(%raytest $= "0")
{
// We didn't hit anything so we can move forward! Moving in 2-unit scales so we don't constantly start/stop
%this.setMoveDestination(VectorAdd(%pos, VectorScale(%newVec, 10)));
}
}
}
%this.schedule(1000, "checkMovement");
return;
}Step 4. Some SwarmPlayer tweaks
A few more adjustments to the datablock in art/datablocks/aiPlayer.cs:
datablock PlayerData(SwarmPlayer : DefaultPlayerData)
{
shootingDelay = 2000;
decalData = 0;
maxForwardSpeed = 4;
};We're disabling the footsteps (largely because I thought they might have been related to the crash) by setting the decalData to 0, and limiting the maxForwardSpeed to 4. This both allows our player to outrun them AND keeps them from jittering as they move forward / rethink their next position.About the author
President and Founder of The Muteki Corporation Makers of such fine iPhone games as MazeFinger, Topple 2, and The Battle of Pirate Bay!
#2
03/10/2011 (9:44 am)
Lovin' these tutorials. I worked through these two pretty quickly, I'm gonna build upon them. I added a spotlight mounted to the player and a beacon light at his feet, since in dim maps it gets difficult to find him sometimes. I was working around with the swarmer's logic, trying to make it so that if the character is, say, around a corner or somewhere where simple obstacle avoidance won't really help a bot, that the bot would move to a nearby marker, and then possibly follow a path or perform some other task. Unfortunately I wasn't able to get it working. 
Torque Owner Richard Ranft
Roostertail Games
No clue in the debugger on the crash, eh? Bummer.