Hello.
So, I've managed to make a ragdoll walk by means of constraining its bones to a NPC's bones, like following its animation cycle(s). But not all models have an NPC script where I can call on certain actions (enumerations). I'm now looking to do stuff with the TF2 characters, for example the Scout. As a start I'm looking to see about making a Scout run around a bit. However, there doesn't seem to be any Scout NPC that I can send walk/run commands to. Tried creating various npc_ with the Scout model, but either it resulted in ERROR or the NPC's default model.
I thought of an alternative which is to play the running animation on a prop while applying the appropriate velocity. However, there seem to be a few problems.
1: I try to play a running sequence with ResetSequence (in a timer loop) but the Scout isn't animating. I tried other animations but none were of that running type.
2: I spawn the Scout as a prop_physics so that SetVelocity or ApplyForce will work on him, but he seems to tilt to one side at first, then rolls around as he is being pushed. What other entity classes can I use instead? Or should I constrain him to another physics object that doesn't roll?
Yo. TF2 models uses an entirely different set of animations/activities as Garry's Mod, so you need to make sure you set all those accordingly.
There's a tool called the animated props tool. Try it. It lets you set animations on props/ragdolls.
You know, I was a little skeptical if buu342 understood what I really wanted, but turns out the tool was more or less what I was looking for. And as wauterboi said, yeah, there does seem to be more needed for "player" animations.
After studying the code from the tool, I found that I just needed to add SetPoseParameter("move_x",1) and, well, now the Scout isn't standing still anymore, but more like running still. That is he seems to be paused on his first frame in the running animation (one leg up).
I changed the class to prop_dynamic and now he is animating properly. Down side is not being able to push him about. But I guess that means I'll have to attach the Scout to another physics object to push around.
Well, looks like I've run into a new problem. When trying to "attach" the bones of the ragdoll Scout onto the dynamic Scout, the limbs do snap to the position of the Scout's running animation's First Frame. The ragdoll does not seem to follow the animation fully to making the ragdoll run with the dynamic. This is what I've used in a timer loop (0.01 seconds):
[code]
for i=0,ragdoll:GetPhysicsObjectCount()-1 do
local phys = ragdoll:GetPhysicsObjectNum(i)
local b = ragdoll:TranslatePhysBoneToBone(i)
local pos,ang = animBase:GetBonePosition(b)
phys:EnableMotion(true)
phys:Wake()
phys:SetAngles(ang)
phys:SetPos(pos)
if string.sub(ragdoll:GetBoneName(b),1,4) == "prp_" then
phys:EnableMotion(false)
phys:Wake()
end
end
[/code]
Are there any alternative "attachment" functions I should look for if I want the ragdoll to mimic the animation being played?
Ah, hey, look what I found from a long time ago!
[code]function GM:HandlePlayerJumping(pl)
if not pl.anim_Jumping and not pl:OnGround() and pl:WaterLevel() <= 0 then
if not pl.anim_GroundTime then
pl.anim_GroundTime = CurTime()
else --[[if CurTime() - pl.anim_GroundTime > 0.2 then]]
pl.anim_Jumping = true
pl.anim_FirstJumpFrame = false
pl.anim_JumpStartTime = 0
end
end
if pl.anim_Jumping then
local firstjumpframe = pl.anim_FirstJumpFrame
if pl.anim_FirstJumpFrame then
pl.anim_FirstJumpFrame = false
pl:AnimRestartMainSequence()
end
if pl:WaterLevel() >= 2 or --[[(CurTime() - pl.anim_JumpStartTime > 0.2 and]] pl:OnGround() --[[)]] then
pl.anim_Jumping = false
pl.anim_GroundTime = nil
pl:AnimRestartMainSequence()
if pl:OnGround() then
pl:AnimRestartGesture(GESTURE_SLOT_JUMP, ACT_MP_JUMP_LAND, true)
end
end
if pl.anim_Jumping then
if pl.anim_JumpStartTime == 0 then
if pl.anim_Airwalk then
pl.anim_CalcIdeal = ACT_MP_AIRWALK
else
return false
end
elseif not firstjumpframe and CurTime() - pl.anim_JumpStartTime > pl:SequenceDuration() then
pl.anim_CalcIdeal = ACT_MP_JUMP_FLOAT
else
pl.anim_CalcIdeal = ACT_MP_JUMP_START
end
return true
end
end
pl.anim_Airwalk = false
return false
end
function GM:HandlePlayerDucking(pl, vel)
if pl:Crouching() then
local len2d = vel:Length2D()
if len2d > 0.5 then
pl.anim_CalcIdeal = (pl.anim_Deployed and ACT_MP_CROUCH_DEPLOYED) or ACT_MP_CROUCHWALK
else
pl.anim_CalcIdeal = (pl.anim_Deployed and ACT_MP_CROUCH_DEPLOYED_IDLE) or ACT_MP_CROUCH_IDLE
end
return true
end
return false
end
function GM:HandlePlayerSwimming(pl)
if pl:WaterLevel() >= 2 then
if pl.anim_FirstSwimFrame then
pl:AnimRestartMainSequence()
pl.anim_FirstSwimFrame = false
end
pl.anim_InSwim = true
pl.anim_CalcIdeal = (pl.anim_Deployed and ACT_MP_SWIM_DEPLOYED) or ACT_MP_SWIM
return true
else
pl.anim_InSwim = false
if not pl.anim_FirstSwimFrame then
pl.anim_FirstSwimFrame = true
end
end
return false
end
function GM:UpdateAnimation(pl, velocity, maxseqgroundspeed)
local maxspeed = 200
if (pl:OnGround() and pl:Crouching()) then
maxspeed = maxspeed * 0.3
elseif pl:WaterLevel() > 1 then
maxspeed = maxspeed * 0.8
end
local vel = 1 * velocity
vel:Rotate(Angle(0,-pl:EyeAngles().y,0))
vel:Rotate(Angle(-vel:Angle().p,0,0))
pl:SetPoseParameter("move_x", vel.x / maxspeed)
pl:SetPoseParameter("move_y", -vel.y / maxspeed)
local pitch = math.Clamp(math.NormalizeAngle(-pl:EyeAngles().p), -45, 90)
pl:SetPoseParameter("body_pitch", pitch)
if not pl.PlayerBodyYaw or not pl.TargetBodyYaw then
pl.TargetBodyYaw = pl:EyeAngles().y
pl.PlayerBodyYaw = pl.TargetBodyYaw
end
local diff
diff = pl.PlayerBodyYaw - pl:EyeAngles().y
if velocity:Length2D() > 0.5 or diff > 45 or diff < -45 then
pl.TargetBodyYaw = pl:EyeAngles().y
end
local d = pl.TargetBodyYaw - pl.PlayerBodyYaw
if d > 180 then
pl.PlayerBodyYaw = math.NormalizeAngle(Lerp(0.2, pl.PlayerBodyYaw+360, pl.TargetBodyYaw))
elseif d < -180 then
pl.PlayerBodyYaw = math.NormalizeAngle(Lerp(0.2, pl.PlayerBodyYaw-360, pl.TargetBodyYaw))
else
pl.PlayerBodyYaw = Lerp(0.2, pl.PlayerBodyYaw, pl.TargetBodyYaw)
end
pl:SetPoseParameter("body_yaw", diff)
if CLIENT then
pl:SetRenderAngles(Angle(0, pl.PlayerBodyYaw, 0))
--pl:SetRenderAngles(Angle(0, pl:EyeAngles().y, 0))
end
end
function GM:CalcMainActivity(pl, vel)
pl.anim_CalcIdeal = (pl.anim_Deployed and ACT_MP_DEPLOYED_IDLE) or ACT_MP_STAND_IDLE
pl.anim_CalcSeqOverride = -1
if
self:HandlePlayerDriving(pl) or
self:HandlePlayerSwimming(pl) or
self:HandlePlayerJumping(pl) or
self:HandlePlayerDucking(pl, vel) then
-- do nothing
else
local len2d = vel:Length2D()
if len2d > 0.5 then
pl.anim_CalcIdeal = (pl.anim_Deployed and ACT_MP_DEPLOYED) or ACT_MP_RUN
end
end
return pl.anim_CalcIdeal, pl.anim_CalcSeqOverride
end
function GM:DoAnimationEvent(pl, event, data)
local w = pl:GetActiveWeapon()
if event == PLAYERANIMEVENT_JUMP then
pl.anim_Jumping = true
pl.anim_FirstJumpFrame = true
pl.anim_JumpStartTime = CurTime()
pl:AnimRestartMainSequence()
return ACT_INVALID
end
end
local meta = FindMetaTable("Weapon")
local OldSendWeaponAnim = meta.SendWeaponAnim
function meta:SendWeaponAnim(act)
if not act then return end
--MsgN(Format("SendWeaponAnim %d %s",act,tostring(self)))
if IsValid(self.Owner) and self.Owner:IsPlayer() and IsValid(self.Owner:GetViewModel()) and self.ViewModelOverride then
self:SetModel(self.ViewModelOverride)
self.Owner:GetViewModel():SetModel(self.ViewModelOverride)
end
OldSendWeaponAnim(self,act)
end[/code]
Kilburn helped me put this together. It's for player animations, but perhaps you can pick stuff from it.
[editline]17th March 2015[/editline]
Oops, it's missing the translations for activities, and I can't find TF2-specific activities. HMM. Working on it!
Sorry, you need to Log In to post a reply to this thread.