Simple Faking Melee with Raycasts
by Steve Acaster · 09/11/2010 (9:59 pm) · 23 comments
First up, the concept.
1. Use/fire an attack with a melee weapon
2. Play the animation of the attack
3. Raycast to see if anything is hit
4. Dole out damage if there was a hit
Secondly, what you'll need.
1. A melee weapon model
2. An animation for the melee attack
3. All necessary scripts to have the melee weapon supported by your player/Ai (inventory listings, maxInv[yourMeleeWeapon] = 1; in player.cs etc)
4. The melee weapon's script!
If they've run out of bullets then the player might be on a sticky wicket ... So in this example, we'll be using a cricketbat.
The cs file for art/datablocks is fairly similar to other weapons, except of course it doesn't require ammunition, the reload FSM is swapped to re-ready the weapon post fire.
The meat of the script is in the scripts cs file where we swap bullets for a raycast.
You might want to actually make the animation play with the weapon's own recoil system in it's fire state, or like here, use an animation thread. If you find that your animation is coming out mangled because you're using a blend animation, then use the stateRecoil[#] for the animation and let the engine do the work on aligning the spine nodes correctly.
There should be a slight delay between firing the weapon/animation and actually doing the attack, so to simulate the initial swing of the weapon before it strikes. This should be dependant on how long your animation actually takes to strike out. In this example I'm using a fast strike off the forward foot towards Silly Mid-Off, so there's very little pull back for swing, and thus the delay is a mere third of a second before the test for contact and damage is scheduled.
Of course you'll want to check against something for recording hits, as well as have scenery stop the attack - unless you've a special love of wallhacks ...
That about covers everything ... notice no dif in the above - embrace the future of static meshes. You could throw in the whole of shapebase if you want to drop players, vehicles, staticShape, etc.
You could use forwardVector but for this example it's a simple vector from the EyeNode, which goes out a mere 2 metres here. So if you're looking at the target and it's 2 units away, it'll get hit, otherwise it's a miss.
If you use a simple applyDamage() and the player gets killed the server will have issues dealing with the death - so it's important to deal out damage using the standard formula of %col.damage(%science_goes_here).
And finally, add an impulse to the attack to send the enemy flying over the boundary for a six!
And that's pretty much that ... remember to exec any new *.cs files that you've created.
You could of course ditch the whole idea of using weapons as the classic Torque weapon-scripted-objects, maybe use a simple punch attack where the keybind literally just fires the attack and animation, cutting out the whole weapons system and goes straight for the raycast as a function.
To get an Ai to use a melee type attack, is similar enough, just make it check whether the conditions are right for it to do so during it's think() cycle. Remember you'll have to check whether or not it's target is in range with a raycast to determine if it's worth a melee attack.
[edited] to clear up a few things, put a couple of warnings for animation problems you might stumble across and generally make it a little more complete for a novice
1. Use/fire an attack with a melee weapon
2. Play the animation of the attack
3. Raycast to see if anything is hit
4. Dole out damage if there was a hit
Secondly, what you'll need.
1. A melee weapon model
2. An animation for the melee attack
3. All necessary scripts to have the melee weapon supported by your player/Ai (inventory listings, maxInv[yourMeleeWeapon] = 1; in player.cs etc)
4. The melee weapon's script!
If they've run out of bullets then the player might be on a sticky wicket ... So in this example, we'll be using a cricketbat.
The cs file for art/datablocks is fairly similar to other weapons, except of course it doesn't require ammunition, the reload FSM is swapped to re-ready the weapon post fire.
datablock ItemData(cricketbat)
{
category = "Weapon";
className = "Weapon";
shapeFile = "art/shapes/weapons/yorks/cricketbat.dts";
mass = 12;
elasticity = 0.2;
friction = 0.6;
maxVelocity = "15.0";
pickUpName = "Cricket Bat";
image = cricketbatImage;
description = "Cricket Bat";
};
datablock ShapeBaseImageData(cricketbatImage)
{
shapeFile = "art/shapes/weapons/yorks/cricketbat.dts";
mountPoint = 0;
eyeOffset = "0 0 0"; // 0.46=right/left 0.5=forward/backward, -0.5=up/down
// correctMuzzleVector = true;//we don't have a muzzle for a melee weapon!
className = "WeaponImage";
item = cricketbat;
stateName[0] = "Activate";
stateTransitionOnTimeout[0] = "Ready";
stateTimeoutValue[0] = 0.5;
stateSequence[0] = "Activate";
stateSound[0] = DrawWeaponSound;
stateName[1] = "Ready";
stateTransitionOnTriggerDown[1] = "Fire";
stateName[2] = "Fire";
stateTransitionOnTimeout[2] = "PostFire";
stateTimeoutValue[2] = 0.8;
// stateRecoil[2] = MediumRecoil;
//you can use the stateRecoil if you want it to play the attack animation
//and have it suitably linked in the player cs,
//see below for alternative
stateAllowImageChange[2] = false;
stateScript[2] = "onFire";
stateSound[2] = meleeFireSound;
stateName[3] = "PostFire";
stateTransitionOnTimeout[3] = "Ready";
stateTimeoutValue[3] = 0.1;//0.01 for 4 rps
stateAllowImageChange[3] = false;
};The meat of the script is in the scripts cs file where we swap bullets for a raycast.
function cricketbatImage::onFire(%this, %obj, %slot)
{
%obj.setActionThread("melee");
%this.schedule(300, "Melee_attack", %obj);
}You might want to actually make the animation play with the weapon's own recoil system in it's fire state, or like here, use an animation thread. If you find that your animation is coming out mangled because you're using a blend animation, then use the stateRecoil[#] for the animation and let the engine do the work on aligning the spine nodes correctly.
There should be a slight delay between firing the weapon/animation and actually doing the attack, so to simulate the initial swing of the weapon before it strikes. This should be dependant on how long your animation actually takes to strike out. In this example I'm using a fast strike off the forward foot towards Silly Mid-Off, so there's very little pull back for swing, and thus the delay is a mere third of a second before the test for contact and damage is scheduled.
Of course you'll want to check against something for recording hits, as well as have scenery stop the attack - unless you've a special love of wallhacks ...
$melee_check2hit = $TypeMasks::VehicleObjectType | $TypeMasks::PlayerObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::StaticTSObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForestObjectType;
That about covers everything ... notice no dif in the above - embrace the future of static meshes. You could throw in the whole of shapebase if you want to drop players, vehicles, staticShape, etc.
You could use forwardVector but for this example it's a simple vector from the EyeNode, which goes out a mere 2 metres here. So if you're looking at the target and it's 2 units away, it'll get hit, otherwise it's a miss.
function CricketBatImage::Melee_Attack(%this, %obj)
{
%eyeVec = %obj.getEyeVector();
%startPos = %obj.getEyePoint();
%endPos = VectorAdd(%startPos, VectorScale(%eyeVec, 2));
%target = ContainerRayCast(%startPos, %endPos, $melee_check2hit, %obj);
%col = firstWord(%target);
if(%col == 0)
return;
echo(%col);
//return the ID of what we've hit
// Apply damage to the object all shape base objects
if (%col.getType() & $TypeMasks::ShapeBaseObjectType)
{
%col.damage(%obj, %pos, 15, "Cricket Bat Smack");
echo(%col.getname());
%vpos = %col.getWorldBoxCenter();
%pushDirection = VectorSub(%vpos,%obj.getWorldBoxCenter());
%pushDirection = VectorNormalize(%pushDirection);
%pushVec = VectorScale(%pushDirection,1000);
%pushVec= getwords(%pushVec,0,1);
%col.applyImpulse(%vpos, %pushVec);
}
}If you use a simple applyDamage() and the player gets killed the server will have issues dealing with the death - so it's important to deal out damage using the standard formula of %col.damage(%science_goes_here).
And finally, add an impulse to the attack to send the enemy flying over the boundary for a six!
And that's pretty much that ... remember to exec any new *.cs files that you've created.
You could of course ditch the whole idea of using weapons as the classic Torque weapon-scripted-objects, maybe use a simple punch attack where the keybind literally just fires the attack and animation, cutting out the whole weapons system and goes straight for the raycast as a function.
To get an Ai to use a melee type attack, is similar enough, just make it check whether the conditions are right for it to do so during it's think() cycle. Remember you'll have to check whether or not it's target is in range with a raycast to determine if it's worth a melee attack.
[edited] to clear up a few things, put a couple of warnings for animation problems you might stumble across and generally make it a little more complete for a novice
About the author
One Bloke ... In His Bedroom ... Making Indie Games ...
#2
09/11/2010 (10:50 pm)
This is pretty much how I was doing it, who needs fancy collision boxes for a melee weapon anyway :)
#4
09/12/2010 (9:12 am)
Thanks for sharing Steve.
#5
09/12/2010 (11:01 am)
This is a cool resource. Verry helpfull. Ty Steve.
#6
This also allows much better control than using invisible short ranged/lived projectiles.
09/12/2010 (12:11 pm)
Thumbs up for thinking outside the box! This also allows much better control than using invisible short ranged/lived projectiles.
#7
Quick question, what Type mask would you use for physics objects, I tried
$TypeMasks::PhysicsObjectType PhysicsShapeObjectType and PhysicsShapeType.
the raycast doesnt damage physics objects
any advice?
09/13/2010 (9:37 pm)
Thanks for the resource Steve, it has been a big help to me.Quick question, what Type mask would you use for physics objects, I tried
$TypeMasks::PhysicsObjectType PhysicsShapeObjectType and PhysicsShapeType.
the raycast doesnt damage physics objects
any advice?
#8
ObjectTypes.h doesn't have any listings for a physics object, so you could try adding the appropriate item there as a typemask - but I'm completely guessing here.
09/13/2010 (10:05 pm)
I've never used physics yet ...ObjectTypes.h doesn't have any listings for a physics object, so you could try adding the appropriate item there as a typemask - but I'm completely guessing here.
#9
09/14/2010 (3:38 am)
What is the name of the physics object class? If T3D hasn't changed this aspect of things (which it probably has), RigidShape is a descendant of ShapeBase, so you could use the ShapeBase mask. I remember having to add my own RigidShapeMask to TGE to get collision against Rigids specifically.
#11
09/27/2010 (9:48 pm)
in order to play the melee animation while moving, does it have to be blended? I have tried setting the melee to highest priority but no change.
#12
a) create a new file under /art/datablocks/weapons/cricketbat.cs
b) paste in the Datablock and Image info from above
c) add a line to art/datablocks/datablockExec.cs ... to exec the new cricketbat.cs file
d) open scripts/server/weapon.cs and add the onFire and Melee_Attack functions
e) Where does the $melee_check2hit definition you have go ? At the top of the ::Melee_Attack() function ?
EDIT: I added the above definition to the top of the ::Melee_Attack() function and it seems to work alright.
Just need to add a small aiming reticle, to know where I am aiming ...
01/30/2011 (9:04 pm)
Please correct me if I'm mis-understanding;a) create a new file under /art/datablocks/weapons/cricketbat.cs
b) paste in the Datablock and Image info from above
c) add a line to art/datablocks/datablockExec.cs ... to exec the new cricketbat.cs file
d) open scripts/server/weapon.cs and add the onFire and Melee_Attack functions
e) Where does the $melee_check2hit definition you have go ? At the top of the ::Melee_Attack() function ?
$melee_check2hit = $TypeMasks::VehicleObjectType | $TypeMasks::PlayerObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::StaticTSObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForestObjectType;
EDIT: I added the above definition to the top of the ::Melee_Attack() function and it seems to work alright.
Just need to add a small aiming reticle, to know where I am aiming ...
#13
02/08/2011 (4:51 am)
Hey steve, did you write anything to clear up the log saying that it couldn't find any ammo in the inventory based on the weapon type? If not, I'm about to do this so I'll post it here.
#14
Thanks for the tutorial, Ive got everything working nicely except one thing,
I need to change the root animation so the hands are holding the sword rather than in gun position.
ive made the animation and it works ok in shape editor but i cant find where / how to set the new root animation on selecting the weapon.
any help much appreciated :)
02/19/2011 (1:35 pm)
Hello allThanks for the tutorial, Ive got everything working nicely except one thing,
I need to change the root animation so the hands are holding the sword rather than in gun position.
ive made the animation and it works ok in shape editor but i cant find where / how to set the new root animation on selecting the weapon.
any help much appreciated :)
#15
03/23/2011 (12:30 pm)
Amazing, I can't wait to try this out after work. This is exactly what I was looking for thanks!!
#16
03/25/2011 (1:25 pm)
Steve wins once again!
#17
[edit]ming, sorry for the dead post but did you ever figure out your animation problem?
and my understanding is the raycast is just hitting a point like a bullet, correct? so you would have to be aiming perfectly? is there a way to set up a degree in the vectors that will damage everything within a 25 degree radius of the eye node?
05/18/2011 (7:11 pm)
hey steve i never got a chance to try this yet. Think itll work with the 1.1p?[edit]ming, sorry for the dead post but did you ever figure out your animation problem?
and my understanding is the raycast is just hitting a point like a bullet, correct? so you would have to be aiming perfectly? is there a way to set up a degree in the vectors that will damage everything within a 25 degree radius of the eye node?
#18
05/18/2011 (7:25 pm)
Quote:Yes. Script works the same in all versions of the engine.
i never got a chance to try this yet. Think itll work with the 1.1p?
#19
05/22/2011 (10:14 pm)
thanks michael im going to try it tonight and tomorrow. this will be my third stab at melee. i had the fake projectiles. since you have been around the scene a long time what would you recommend for melee, faking projectiles or raycasting infront of the eye node? any experience with them?
#20
05/24/2011 (8:59 am)
@Jordan: both methods work. I'd say that using a raycast can give a bit more precision, and it's easier to make an invisible projectile hit more things - but it really depends on which works best for you in a given situation. 
Torque Owner Christian S
Oak-Entertainment