Additional animation for player models
Moderator: InsideQC Admins
9 posts
• Page 1 of 1
Additional animation for player models
I've been cracking my head around this for some time and could not find any documents to help me out, so, i'm asking here. I've prepared a very basic player model, with default animations (running, walking, jumping, etc.)
I want to know how can i "implement" some of these animations into the player code. For example, when it jumps, it should play the jumping animation. When strafing, it should play the strafe animation, and so on...
I want to know how can i "implement" some of these animations into the player code. For example, when it jumps, it should play the jumping animation. When strafing, it should play the strafe animation, and so on...
- JasonX
- Posts: 411
- Joined: Tue Apr 21, 2009 2:08 pm
There could be a smarter way to do this but...
The way I've been tackling this for my project is at the very start of the model's qc file where it defines all the frames, you want to modify the frame order to what they are in your model.
For instance, if your first 5 frames are for running you would make it look like:
This gets to be a super pain in the butt when in comes down to counting frames into the hundreds, but oh well.
Then further down in the code, when you are actually telling the game when to play the animation you'll see something that looks like
The first $run1 in the bracket tells the game which frame of animation to run, and the second statement "playerrun2" tells the game what function to do next. The code in the curly bracket ("dosomething") is what tells the game what to do in terms of actually moving the character, or playing a sound, or whatever. Like, if you put "{sound (self, CHAN_VOICE, "fartnoise.wav",1, ATTN_IDLE;}" the game would have your guy fart whenever playerrun1 happened. Well, not really because I don't think that there is actually a "fartnoise.wav", and I might be using the sound(...) function wrong, but you get the idea.
Hence, you have:
I hope this helps, and I'll explain further if you want. I'm sure some of these Inside3D vets could help you more though. I'm brand spanking new to QuakeC myself, but this is how I've been getting my own models into the game so far.[/code]
The way I've been tackling this for my project is at the very start of the model's qc file where it defines all the frames, you want to modify the frame order to what they are in your model.
For instance, if your first 5 frames are for running you would make it look like:
- Code: Select all
$frame run1 run2 run3 run4 run5 etc...
This gets to be a super pain in the butt when in comes down to counting frames into the hundreds, but oh well.
Then further down in the code, when you are actually telling the game when to play the animation you'll see something that looks like
- Code: Select all
void() playerrun1 = [$run1, playerrun2 ] {dosomething;};
The first $run1 in the bracket tells the game which frame of animation to run, and the second statement "playerrun2" tells the game what function to do next. The code in the curly bracket ("dosomething") is what tells the game what to do in terms of actually moving the character, or playing a sound, or whatever. Like, if you put "{sound (self, CHAN_VOICE, "fartnoise.wav",1, ATTN_IDLE;}" the game would have your guy fart whenever playerrun1 happened. Well, not really because I don't think that there is actually a "fartnoise.wav", and I might be using the sound(...) function wrong, but you get the idea.
Hence, you have:
- Code: Select all
void() playerrun1 = [$run1, playerrun2 ] {dosomething;};
void() playerrun2 = [$run2, playerrun3 ] {dosomething;};
void() playerrun3 = [$run3, playerrun4 ] {dosomething;};
void() playerrun4 = [$run4, playerrun5 ] {dosomething;};
void() playerrun5 = [$run5, playerrun1 ] {dosomething;};
I hope this helps, and I'll explain further if you want. I'm sure some of these Inside3D vets could help you more though. I'm brand spanking new to QuakeC myself, but this is how I've been getting my own models into the game so far.[/code]
- Handshakes
- Posts: 11
- Joined: Sun May 30, 2010 6:53 pm
- Location: Detroit, MI
if you take a look at the stock player.qc, you'll see that there is already some rudimentary code to handle standing and running.
the frame functions check if the player is moving or not, and then switches to a different function based on that.
essentially, you'll be doing the same thing, except you'll want to check up on more things than just moving or not.
strafing, however, is not immediately different than simply running, because both those things involve moving. you'll need to check up on the direction of movement and compare it to the facing angle of the player.
you can do this with the dot product.
dot will be anything from 1 (moving in exactly the same direction as you are facing) or -1 (moving in the completely opposite direction as facing). values near 0 indicate strafing.
note that we flatten vec's z portion because we aren't interested if the player is moving up or down and it will mess up the dot product by telling us we are strafing if the player is falling downward.
now that we can tell if the player is strafing or not, we still don't know which direction they are headed in, left or right.
fortunately, we have v_right still available from the makevectors call we made.
it's just a matter of doing a new dot product
if we get a positive value, we're heading right, and negative is left.
if you do not plan on having a backpeddle animation, you could go right to the vec * v_right and skip the v_forward check like this:
for jumping, i would simply create a jump animation that can be broken up into 3 segments.
jumpStart
jumpLoop
jumpEnd
jumpStart should play when you jump and lead into jumpLoop which should play until you land, at which point it should lead into jumpEnd.
first, i'd put a call to jumpStart(); in the PlayerJump() function in client.qc. this will ensure the jump animation plays as soon as the jumping function is called.
jumpLoop should continually check if self.flags & FL_ONGROUND is set and when it is, it should break out to jumpEnd. don't forget to loop back to the running/standing animation when jumpEnd is done.
so that's one way to do it.
the frame functions check if the player is moving or not, and then switches to a different function based on that.
essentially, you'll be doing the same thing, except you'll want to check up on more things than just moving or not.
strafing, however, is not immediately different than simply running, because both those things involve moving. you'll need to check up on the direction of movement and compare it to the facing angle of the player.
you can do this with the dot product.
- Code: Select all
local vector vec;
local float dot;
vec = self.velocity;
vec_z = 0;
vec = normalize(vec);
makevectors(self.angles);
dot = vec*v_forward;
dot will be anything from 1 (moving in exactly the same direction as you are facing) or -1 (moving in the completely opposite direction as facing). values near 0 indicate strafing.
note that we flatten vec's z portion because we aren't interested if the player is moving up or down and it will mess up the dot product by telling us we are strafing if the player is falling downward.
now that we can tell if the player is strafing or not, we still don't know which direction they are headed in, left or right.
fortunately, we have v_right still available from the makevectors call we made.
it's just a matter of doing a new dot product
- Code: Select all
dot = vec * v_right;
if we get a positive value, we're heading right, and negative is left.
if you do not plan on having a backpeddle animation, you could go right to the vec * v_right and skip the v_forward check like this:
- Code: Select all
dot = vec * v_right;
if (fabs(dot) > 0.5)
{
if (dot > 0)
//heading right
else
//heading left
}
else
//moving forward or back
for jumping, i would simply create a jump animation that can be broken up into 3 segments.
jumpStart
jumpLoop
jumpEnd
jumpStart should play when you jump and lead into jumpLoop which should play until you land, at which point it should lead into jumpEnd.
first, i'd put a call to jumpStart(); in the PlayerJump() function in client.qc. this will ensure the jump animation plays as soon as the jumping function is called.
jumpLoop should continually check if self.flags & FL_ONGROUND is set and when it is, it should break out to jumpEnd. don't forget to loop back to the running/standing animation when jumpEnd is done.
so that's one way to do it.
- necros
- Posts: 77
- Joined: Thu Dec 16, 2004 10:32 pm
Thank you so much for the info, necros. I really appreciate it. Do you think that, with a proper animation set, something like Half-Life did is possible?
HL had a running, walking and etc. animation for North, South, East, West, Northeast, Northwest, Southeast, Southwest...
HL had a running, walking and etc. animation for North, South, East, West, Northeast, Northwest, Southeast, Southwest...
- JasonX
- Posts: 411
- Joined: Tue Apr 21, 2009 2:08 pm
Half-Life pulled it off with skeletal blending and bone controllers. It had one walking animation and one running animation. Bone controllers turned the skeleton appropriately.
We don't have this equivelant in Quake yet since a 1997 standard since Jedi Knight is WAY too new.
We don't have this equivelant in Quake yet since a 1997 standard since Jedi Knight is WAY too new.
i should not be here
- leileilol
- Posts: 2783
- Joined: Fri Oct 15, 2004 3:23 am
JasonX wrote:Thank you so much for the info, necros. I really appreciate it. Do you think that, with a proper animation set, something like Half-Life did is possible?
HL had a running, walking and etc. animation for North, South, East, West, Northeast, Northwest, Southeast, Southwest...
I think would be easier to aim for something like Q3 forward, backward and strafe animations.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC
(LordHavoc)
-

frag.machine - Posts: 2090
- Joined: Sat Nov 25, 2006 1:49 pm
it might be possible if you wanted to try something similar to the q3 approach. q3 used 3 separate models for all characters, legs, torso and head and kept each component glued to the other via a specially marked triangle called the tag on each of the other components.
of course, even that will not be quite the same as quake doesn't have tag points like q3 does. either you would have to animate each component so that it always looked correct when a zero'd offset relative to the origin of the main model, or you'd have to hard code position offsets for each frame which would be insane.
one thing you could add is the variable speed walk/run animation like in half life. that's to say, if you were walking into a wall and sliding along it slowly, your running animation would play slower than if you were just running straight ahead.
be wary though because this method can mess up frame interpolation in some engines like fitzquake that use .nextthink to determine how fast/slow to interpolate.
if you look at the stock run frame functions for the player, you'll realize that they always run at 0.1 second intervals (the frame macro sets that automatically).
while setting nextthink to higher or lower settings would in theory make the player run faster or slower, this isn't ideal because there are important checks that have to be made in that function that might be delayed too long if you are running slowly.
so we instead go the opposite direction.
we set self.nextthink = time;.
this will make the frame function run every game frame. (you can scale it back to maybe time + 0.02 or 0.04 if you want to increase performance, but honestly, any modern machine should handle it no problem)
next we have to create two new float .variables.
.frameCountGoal and .frameCount.
we're basically creating a proxy instead of incrementing self.walkframe.
.frameCount will increase by 1 modified by frametime every time the function is called, much like .frame was originally.
.frameCountGoal will be dynamically calculated based on movement speed.
finally, we move the self.walkframe = self.walkframe + 1 inside an if statement that checks when self.frameCount > self.frameCountGoal.
it might be wise to create two running animations if you go this route: one full tilt sprinting run and a more jogging run so you can switch from one to the other based on speed (you'll look stupid if you're jogging at light speed or sprinting like molasses). if you make the two animations have the same number of frames, you can even switch between the two preserving relative frames so that you don't always start each animation on the first frame making it look more natural.[/i]
of course, even that will not be quite the same as quake doesn't have tag points like q3 does. either you would have to animate each component so that it always looked correct when a zero'd offset relative to the origin of the main model, or you'd have to hard code position offsets for each frame which would be insane.
one thing you could add is the variable speed walk/run animation like in half life. that's to say, if you were walking into a wall and sliding along it slowly, your running animation would play slower than if you were just running straight ahead.
be wary though because this method can mess up frame interpolation in some engines like fitzquake that use .nextthink to determine how fast/slow to interpolate.
if you look at the stock run frame functions for the player, you'll realize that they always run at 0.1 second intervals (the frame macro sets that automatically).
while setting nextthink to higher or lower settings would in theory make the player run faster or slower, this isn't ideal because there are important checks that have to be made in that function that might be delayed too long if you are running slowly.
so we instead go the opposite direction.
we set self.nextthink = time;.
this will make the frame function run every game frame. (you can scale it back to maybe time + 0.02 or 0.04 if you want to increase performance, but honestly, any modern machine should handle it no problem)
next we have to create two new float .variables.
.frameCountGoal and .frameCount.
we're basically creating a proxy instead of incrementing self.walkframe.
.frameCount will increase by 1 modified by frametime every time the function is called, much like .frame was originally.
.frameCountGoal will be dynamically calculated based on movement speed.
finally, we move the self.walkframe = self.walkframe + 1 inside an if statement that checks when self.frameCount > self.frameCountGoal.
it might be wise to create two running animations if you go this route: one full tilt sprinting run and a more jogging run so you can switch from one to the other based on speed (you'll look stupid if you're jogging at light speed or sprinting like molasses). if you make the two animations have the same number of frames, you can even switch between the two preserving relative frames so that you don't always start each animation on the first frame making it look more natural.[/i]
Last edited by necros on Fri Aug 20, 2010 7:04 pm, edited 2 times in total.
- necros
- Posts: 77
- Joined: Thu Dec 16, 2004 10:32 pm
necros wrote:for jumping, i would simply create a jump animation that can be broken up into 3 segments.
jumpStart
jumpLoop
jumpEnd
jumpStart should play when you jump and lead into jumpLoop which should play until you land, at which point it should lead into jumpEnd.
first, i'd put a call to jumpStart(); in the PlayerJump() function in client.qc. this will ensure the jump animation plays as soon as the jumping function is called.
jumpLoop should continually check if self.flags & FL_ONGROUND is set and when it is, it should break out to jumpEnd. don't forget to loop back to the running/standing animation when jumpEnd is done.
so that's one way to do it.
Where should i check the FL_ONGROUND?
- JasonX
- Posts: 411
- Joined: Tue Apr 21, 2009 2:08 pm
9 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 1 guest