PDA

View Full Version : Coding tutorials from yours truly (melee AI)


Rico
03-21-2004, 03:45 PM
Due to popular request I have decided to post this for everyone to see instead of answering with a PM:

I will now explain to all willing ears how to create a basic enemy that will try to attack the player whenever he is in melee range. Take heed and remember that this code I am about to share with you is in beta stage and as a consequence it will contain several bugs and flaws, among them the fact that enemies will not attack the player if he stands completely still (no keys are pressed) and the fact that the enemy will sometimes attack VERY fast if the player moves too much. With all these things explained I will now explain how it is that I made a melee only enemy (and other enemies will also attack using melee if they are close enough).

First I must explain the logic behind the method. I started with the idea that enemies should try to get close to the player, as close as possible with the settings we were supplied by Remedy for the AI. After managing to this the idea then progressed into actually making the enemies attack when they were in close range and not running away (they always think they have a gun in their hands, very dumb). I thought about it for a while and eventually I came up with what I call a "melee aura". Basically this is an area around the player that signals the enemy to attack whenever he comes into range. Now I will go on to the in-depth explanation of how to code this:


We will start by changing an enemy so that he comes close to you instead of running away. The simplest way of doing this is to just use the same settings as the Boozehound NPC and then applying them to the desired enemy. The result is then an enemy who follows you around wherever you try to go. This is done by lowering the FOV to low ranges as well as changing the enemy's cone of fire (the angle at which they start firing). You really shouldn't bother with this, just use the boozehound settings as they are the most appropriate at the moment.

You now have an enemy that follows you around.

Here comes the crucial part: You want to create an aura around the player that makes the enemies want to attack you.

To do this we will start by making a projectile. Make a new projectile called PlayerAura.txt in the projectiles folder. Don't forget to create a folder for it so your game doesn't crash (place empty dat file inside, I expect you to know this already). Inside you will paste the following code:



#include <..\projectiles.h>
#include <..\projectileanimid.h>
#include <..\object_lods.h>

// -------------------------------------------------------------------
// Baseballbat melee hit projectile
// -------------------------------------------------------------------

[LOD]
{
[Geometry] exportdata = ..\dummy\dummy_l0.kf2;
//[Geometry] exportdata = ..\..\weapons\molotov\Molotov_L0.kf2;
Distance = 0.001 ;
}

[Animationset]
{
[Animation] Index = PROJECTILEANIM_DEFAULT; Filename = ..\dummy\dummy_l0.kf2;

[Animation] Index = PROJECTILEANIM_RICOCHET; Filename = ..\dummy\dummy_l0.kf2; // ..\dummy\dummy_l0.kf2;

[Animation] Index = PROJECTILEANIM_EXPLODE; Filename = ..\dummy\dummy_l0.kf2; // ..\dummy\dummy_l0.kf2;

[Animation] Index = PROJECTILEANIM_FROZEN; Filename = ..\dummy\dummy_l0.kf2; // ..\dummy\dummy_l0.kf2;
}

[Attributes]
Direction = ( 0 , 0 , 1 );
OffsetType = USE_PROJECTILEOFFSET_BULLET; //none
Accuracy = 125;
Speed = 60; //20
SpeedRandom = 0%;
Damage = 0.0001; // small amount of damage... you don't want to kill the enemy
RicochetMaximumAngle = 0;
RicochetSpeedMultiplier = 0.00001;
RicochetMinimumSpeed = 1;
RicochetUntilOnGround = false; // true for projectiles that create level items
VisibilityMaximumSpeed = 100; // put this to 100 to see visualization
// (0..1) interpolator for skin target penetration multipliers
PenetrationValue = 1.0;

SmallProjectile = TRUE;
Debris = FALSE;
RandomizeDirection = TRUE;
RandomizeRotation = FALSE;

CollisionExplosion = TRUE;
DelayedExplosion = TRUE;
ExplosionDelay = 0.07;//0.009
GravityMultiplier = 0.0;//0.0
UseLookAt = FALSE;
DamagesCharacter = true;//true
ItemsToCache = 0;
MeshesToCache = 0;
DeathCause = PROJECTILEDEATH_RANDOM;
BloodProjectile = FALSE; // uses same detail level settings as debris but is not debris
DamageType = DAMAGETYPE_SMALL; // change to a damagetype you aren't using already
DamageTypeWithArmor = DAMAGETYPE_SMALL; // change to a damagetype you aren't using already





Now the explanation: The projectile is there so that the enemy has some way to receive the input from the player. These projectiles will be emitted from all around the player and will be what triggers the reaction from the enemy. The damagetype we are using for this projectile CAN ONLY BE USED FOR THIS PURPOSE ALONE. This means it cannot be used by any other projectiles unless you want the enemy to attack you with a melee attack when hit. I will move on to explain why we used a damagetype further along.

Now we must move on to the player skin file and make some changes to the modifiers present there. Basically what you want is to add the following code to most of the animations used for the player (running, crouching, jumping, standing, turning, etc). These will be the chunks where the projectiles are generated. The aura is created by a lot of projectiles which are created at random directions from the bone of origin, in which case I chose Bip01 Spine. Add in this chunk of code:

(I added these changes to male_player.h)


[Message] Frame = 0; String = "this->P_CreateProjectileToBone( PlayerAura, 20, \"Bip01 Spine\");";


I added this exact same line to most of the animation chunks I already mentioned before. This method is ackward because if the player doesn't move it means the aura won't be generated (the stand animation doesn't seem to be generating the projectiles, pose doesn't work either). However I have already thought of a way of fixing this by creating a subanimation that executes the projectile generation code instead of creating them right in the animation chunk. The subanim will contain little to no changes in motion (maybe a facial animation, who knows). However take heed as I have not tried this method yet due to lack of new animations so try it at your own risk.

Right... Now lets move on to the actual attack being executed:

(male.txt skeleton file)



[Animation] Index = CHARANIM_AURADAMAGE; Filename = "anim\GetHit_Very_Small.kf2";
[Properties]
{
[Reference] Name = GetHit_Aura;
Message] Frame = 0; String = "this->c_subanimate(Sub_Punch_Player);";
}



Now, remember we cannot make new damagetypes so in reality CHARANIM_AURADAMAGE cannot really be used. Instead replace this with whichever damagetype animation you decided to change(CHARANIM_SMALLDAMAGE for example). Then go up to your subanimation chunk in the male.txt and add in the following new subanimation:



[Animation] Index = SUBANIM_SHOOTPUNCH; Filename = "anim\shoot_grenade_forward.kf2";
[Properties]
{
[Reference] Name = Sub_Punch_Player;
[Message] Frame = 0; String = "this->cam_animateplayerparented(BT_Circle_kick);";
//[Message] Frame = 0; String = "maxpayne_gamemode->GM_SetPlayerControls(false);";
[Message] Frame = 0; String = "RightHandWeapon->WS_UsePrimary( true );";
[Message] Frame = 0; String = "RightHandWeapon->WS_Hide( true );";
//[Message] Frame = 0; String = "LeftHandWeapon->WS_Hide( true );";
[Message] Frame = 20; String = "RightHandWeapon->WS_AnimateShooting(" WEAPONANIM_SHOOT "," WEAPONANIM_SHOOTEMPTY ");";
// new change [Message] Frame = 20; String = "this->P_CreateProjectileToBone( BulletMelee3, 1, \"Bip01 R Hand\");";
[Message] Frame = 20; String = "this->A_Play3DSound( weapons, player_shoot_melee, \"\" );";
[Message] Frame = 22; String = "RightHandWeapon->WS_AnimateShooting(" WEAPONANIM_SHOOT "," WEAPONANIM_SHOOTEMPTY ");";
// new change [Message] Frame = 22; String = "this->P_CreateProjectileToBone( BulletMelee3, 1, \"Bip01 R Hand\");";
//[Message] Frame = 35; String = "maxpayne_gamemode->GM_SetPlayerControls(true);";
[Message] Frame = 35; String = "RightHandWeapon->WS_Hide( false );"; //used to be frame 40
//[Message] Frame = 40; String = "LeftHandWeapon->WS_Hide( false );";

}



Basically what you need to know here is that you need to add the actual melee attack to this subanimation chunk. BulletMelee3 are my projectiles that I used to cause damage (the enemy punches the player with this subanimation). I used the throwing animation to simulate a punch without using new animations and the projectiles are created much like you would for any other melee attack. If you are reading this you should probably be able to tell what each line in the above code means.

Why this worked:

MP2 coding is very limited in what you can do, however there are many little tricks you can use to confuse the game into doing what you want it to do and therefore doing interesting things and getting similarly interesting results. What I did here was trick the game into having the PlayerAura projectile tell the enemy to perform a subanimation whenever the projectile caused damage. This command could not have been passed on through the actual projectile file since you cannot execute commands like c_subanimate while inside a projectile because the projectile itself would try to execute the command, and non NPC objects cannot execute animations or C_ commands of any type (character commands). What I did instead was have the projectile perform a specific damage animation after hitting the enemy (very small amount of damage) and then, while inside the damage animation chunk, telling the character to actually perform the desired subanimation (in this case a melee punch attack). So in reality the projectile acts as a messenger, the enemy receives the message after taking damage and then does as he's told and attacks the player.

The aura is generated because the projectiles in PlayerAura explode after 0.07 seconds or on collision. So an enemy across the room does not react to the aura but an enemy close to you does.

Obviously this method needs some refinement but I believe this could help the modding community in some way or another (this will actually help you do a LOT if you're smart about using it, this is not even close to the only use it can be given). I also expect people to use this for hand-to-hand combat mods (I hate kung-fu describing it) and such... you could create enemies that block your attacks and enemies that react to your attacks with counter-attacks. The uses for this code are only limited by your imagination and your capability as a coder.

As a small token of gratitude I expect that anyone who uses my method (or tutorial) to at least include me in the credits for your mod. I do not expect people to ask permission to use it because I shared it to help the modding community advance and create better things. Just mention me in your credits and I will be overjoyed.

If anyone has anything to share about the topic at hand please do it here. I want to turn this thread into a general coding breakthrough thread of sorts. If you have any interesting things you have been able to do or any ideas or questions regarding tough coding issues post them here so we can work on things as a community. I believe there are many coders out there who are just keeping stuff like this to themselves instead of sharing it and helping the community.

I encourage everyone to contribute to this thread.

Thiefmaster
03-21-2004, 04:12 PM
Very nice. Not that i code myself, but the ones who do is in for a real treat..

code_warrior01
03-21-2004, 05:18 PM
Thank you so much Rico, you have saved my entire project. Iw as about to give up, your truly the ****** man hehe http://forums.3drealms.com/ubbthreads/images/graemlins/smile.gif from this base ill be able to do everything I want! :: giggles with joy ::

MrUniq
03-21-2004, 05:21 PM
hey code warrior...i saw how your other post got locked.....too bad, but we appreciate you being a part of the community.

Uisor
03-21-2004, 05:29 PM
well it got locked because it was personal request which could've been made by PM. It's very unlikely that this get's locked unless someone starts misbehaving.

theHunted
03-22-2004, 01:55 AM
what could be interesting to anybody who's planning to use this in a custom made map. instead of creating the projectile from the movement animations of the player (which for instance doesn't even work if the player stands), you could create a fsm with a looping timer in your map, and have it create the projectile with the line
player->P_CreateProjectileToBone(...);
at every iteration of the timer

on the other hand a question to rico: isn't there a idle stand animation, that gets performed when the player stands still, as well?

Rico
03-22-2004, 02:12 AM
I really haven't looked for one but there could possibly be one. I really doubt it however since the player stands motionless unless you hit a key or move the mouse. The head moves and follows targets and enemies around but that is specified with a variable (hardcoded) and does not use an animation chunk so a projectile could not be created then.

As I said however, some of the problems could be fixed by creating a subanimation. The exact results of this I cannot predict just yet...

As an addition though, the code works very well if you are unaware of it's shortcomings. I highly doubt a player would just sit there in front of the enemy waiting to get hit, and I have actually coded my melee only enemies to hit without using that subanimation too. This is mostly a way of making sure they hit when I want them to, otherwise they revert to their old AI code which is less predictable.

Kozak
03-22-2004, 12:58 PM
I already thought of this method but it's an expensive/unrealistic way of doing meleeing attacks. The AI doesn't control it's attack but you do. So I am quite hesitant in using it. Only if there's no other way. But I appreciate that your trying to help the community, lot's of people can learn from this (y).

Rico
03-22-2004, 04:44 PM
If anyone can find a better way please post it here and explain it. Nobody gains anything from people keeping stuff like this to themselves and I expect people to start sharing their work here, otherwise why keep on handing down my work if all people do is take it and run off with it to use as they please without contributing anything in return?

This isn't aimed at anyone, I just thought I'd put the idea out there. The aim of this thread is to create a more open community that shares more than simple tweaks. I would really like to see more big ideas and important changes posted here.

kemical
03-22-2004, 06:18 PM
hey, cool stuff man http://forums.3drealms.com/ubbthreads/images/graemlins/smile.gif
I can't check right now, but I'm pretty sure there is an idle animation you can use, I think I used it before with toggling cameras and pfx on the player, from what I remember, its right at the top of the file (unless it was the turn left / turn right animations)..

Rico
03-23-2004, 12:20 PM
Hmm I checked again to make sure. I don't think there's an idle animation for the player. I need to try out the subanimation method I talked about to see if that yields better results.

theHunted
03-23-2004, 01:05 PM
2_Stand.kf2 maybe?
i'm quite sure there has to be an animation. when you stand still ingame, the whole body does a slight animation with the head turning around randomly.

Rico
03-27-2004, 07:49 PM
Hmm well its definitely not 2_stand because thats the 2 handed stand animation. I think they might be using their passive animations like Idle_bored and stuff like that. I'll give it a try but I don't see any reference to it anywhere in the skeleton files so it might be called through the maps or something. It's od that I can find no reference to the player idle animation though...

Aztec
03-28-2004, 04:22 AM
The idle animation is stand.kf2. I've made a new animation for it so it looks like he breathes while max is in a 'fighting ready' stance.

This is the block in male.txt in the skeleton dir.

[Animation] Index = CHARANIM_STAND; Filename = "anim\Stand.kf2";
[Properties]
{
[Reference] Name = "idle_stand";
}

Kozak
03-28-2004, 04:33 AM
If you do that you would have to do that for all the animations. I mean the enemy has to know if he is near you.
So Hunted's idea of making a looping timer is I think the most clean. If you would do it in code I would make a looping subanim.

Aztec
03-28-2004, 04:58 AM
The animation already loops smoothly, but i've still yet to experiement with triggers and timers as such. Plus i'll be changing all the animations anyway so it doesn't bother me

Rico
03-28-2004, 11:37 AM
Yeah I already mentioned making a subanim for this would be the best way to code it but that requires new anims for me to test with and I have none. Anyways stand.kf2 doesn't seem to work because that is one of the first animations to which I added the projectile generation. I added it to all animations except firing, shootdodges and stuff like that. I also made sure I added it to both combat and 2 handed animations.

Yes, a level timer would be ideal but I am looking into a better way.

Kozak
03-29-2004, 06:53 AM
If anyone is still interested in Melee AI.
I have found out that making a melee enemy in MP scripting becomes kinda messy and gives you an unchallenging enemy. As I stated before I thought that Melee AI is probably only possible with the use of ME2.

I have currently cooked up an at 'first sight' realistic melee enemy that follows you around and can do a range of attacks (when I get the animation for them).

Problem with this method is that the enemy is melee only. So if anyone is interested I could cook up a small tutorial (the code is small) when it's done.

Ghast
03-29-2004, 10:48 AM
Problem with this method is that the enemy is melee only. So if anyone is interested I could cook up a small tutorial (the code is small) when it's done.



yes, please!

Kozak
03-29-2004, 12:32 PM
Oh now I made him not shoot only, I only hope he doesn't become to dumb :-/ http://forums.3drealms.com/ubbthreads/images/graemlins/tongue.gif.

Rico
03-29-2004, 04:58 PM
Post the code if possible.

I am messing around with the idea of implementing the code into both maxed and scripts. This will probably make them smarter and actually try to chase you around levels without limiting their FOV.

I assume you made them perform different attacks by sending messages through projectiles right kozak?

Kozak
03-30-2004, 10:53 AM
Nope. The way I let them attack is to make a set of FSM states where they execute a scrip command. So I just let them perform an animation. I never shift to a kungfu weapon of a kind.
Because I use FSM_randomize I can add an unlimited amount of melee attacks.
Once I get the code fully worked out I'll post the map file.

Ghast
03-30-2004, 11:49 AM
that sounds great Kozak!

I am always shaking my head over the first response seems to be to change Global this or that, when I find the scripting to be the hardest to get expertise with. (remove all melee weapons and errors with using melee as a weapon for AI as examples *LOL*)

I am really looking forward to this!

Rico
03-30-2004, 01:54 PM
Oh mapping changes then? I figured it would have to be done that way. I'm no mapper and in fact id rather stay as far away from MaxEd2 as possible :P

Put out the map file then, Uisor will probably like it.

Ghast
08-22-2004, 12:55 PM
*bump*