Game Development Community

dev|Pro Game Development Curriculum

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.
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
Page «Previous 1 2
#1
09/11/2010 (10:42 pm)
Interesting approach, nice ressource :)
#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 :)
#3
09/12/2010 (8:21 am)
Cricket Bat for dah win!!! 8-}

Nice resource Steve.
#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
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
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
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.
#10
09/14/2010 (11:25 am)
@Donald,
look at physicsDebris
#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
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
02/19/2011 (1:35 pm)
Hello all

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 :)


#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
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:
i never got a chance to try this yet. Think itll work with the 1.1p?
Yes. Script works the same in all versions of the engine.
#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.
Page «Previous 1 2