-
Posts
525 -
Joined
-
Last visited
Content Type
Blogs
Forums
Store
Gallery
Videos
Posts posted by TylerH
-
-
This hard coded state based logic and animation is starting to seem like the wrong idea.
How exactly should you handle the logic of weapons?
For example, going from Idle to Moving, Firing, Reloading, Zooming (iron sights), etc.
Right now I use a large combination of weapon:SetState, weapon:SetAnimation, Get forms of those, etc. Then the weapon's update loop just plays the current animation (which defaults to Idle if nothing is set).
This is the DRAFT DRAFT! pre-pre-pre-alpha version of wepaon.lua and the first weapon inheriting it gold_xix. Just note that the functionality you see in the videos and such was implemented using if statements and hackish stuff in the render loop of the fps script. I am porting over to the actual class-based weapon setup, and so far only have states and animation working, with the functionality already more powerful, as you can see from the code:
weapon.lua
dofile("scripts/utilities.lua") -- Defaults -- States WEAPON_IDLE = 0 WEAPON_TAKE_OUT = 1 WEAPON_PUT_AWAY = 2 WEAPON_FIRE = 3 WEAPON_RELOAD = 4 WEAPON_ZOOM_IDLE = 5 WEAPON_ZOOM_FIRE = 6 WEAPON_TO_ZOOM = 7 WEAPON_TO_NORMAL = 8 WEAPON_MOVING = 9 WEAPON_ZOOM_MOVING = 10 -- Globals WEAPON_SCALE = 0.6 -- Global scale shared by all weapons. Gun specific scaling is multiplied by this factor. It allows guns of different scales to appear similarly sized. -- Variables firstweapon=nil lastweapon=nil -- Weapons Table weapons={} function CreateWeapon( weapon_table ) local weapon = {} -- Sub Tables weapon.Animations = { } -- Table containing all animations weapon.Sounds = { } -- Table containing all sounds weapon.Effects = { } -- Table containing all effects (emitter, muzzle flash, brass, etc.) weapon.Attachments = { } -- Table containing attachments (ironsights, scopes, etc.) -- Properties weapon.AnimationBlending = 0.5 weapon.SwayScale = 0 -- X Axis Movement weapon.BobScale = 0 -- Y Axis Movement weapon.Zoomed = false weapon.Moving = false weapon.AutoReload = false -- Gun will automatically go to reload state if you try to fire with an empty clip -- Sound weapon.Volume = 1.0 weapon.Pitch = 1.0 -- Animation weapon.AnimationStartTime = AppTime() weapon.CurrentAnimation = "" weapon.CurrentAnimationFrame = 0 weapon.Animating = false -- Primary weapon.Primary = { } weapon.Primary.ClipSize = 1 -- Number of rounds in the clip weapon.Primary.Clip = 1 -- Current number of rounds in the clip weapon.Primary.Ammo = 1 -- Current total ammo (not including current clip) weapon.Primary.Damage = 0 -- Damage done per round weapon.Primary.ShotCount = 0 -- Bullets ejected per round weapon.Primary.Cone = 0 -- Spread cone (X,Y) weapon.Primary.Delay = 0 -- Time between current shot and next shot weapon.Primary.NextFire = AppTime() -- Time next shot can be fired -- Secondary weapon.Secondary = { } weapon.Secondary.ClipSize = 0 weapon.Secondary.Clip = 0 weapon.Secondary.Ammo = 0 weapon.Secondary.Damage = 0 weapon.Secondary.ShotCount = 0 weapon.Secondary.Cone = 0 weapon.Secondary.Delay = 0 weapon.Secondary.NextFire = AppTime() -- Finite State Logic weapon.State = 0 -- Weapons's current logic state -- World/View Models weapon.WorldModel = nil -- World View Model weapon.ViewModel = LoadMesh(weapon_table.ViewModel) -- First Person View Model weapon.ViewModel:SetParent(fw.main.camera,0) local vwep_offset, vwep_scale = weapon_table.Offset, weapon_table.Scale weapon.ViewModel:SetPosition(Vec3(-0.01*WEAPON_SCALE,-0.005*WEAPON_SCALE,0.05*WEAPON_SCALE),0) weapon.ViewModel:SetScale(Vec3(0.04*WEAPON_SCALE,0.04*WEAPON_SCALE,0.04*WEAPON_SCALE)) weapon.ViewModel:SetShadowMode(0,1) AppLog("We loaded and positioned the view model...") -- Future AABB Random Culling Fix... --vwep.localaabb.x0=-3 --vwep.localaabb.x1=3 --vwep.localaabb.y0=-3 --vwep.localaabb.y1=3 --vwep.localaabb.z0=-3 --vwep.localaabb.z1=3 --vwep:UpdateAABB() -- Positioning / Offsets weapon.Offset = weapon.ViewModel.position:Copy() local gundisplayposition = weapon.ViewModel:GetPosition() local positionentity = FindChild(weapon.ViewModel,"FIRESPOT") or weapon.ViewModel -- Entity used for firespot location local displayposition = EntityPosition(positionentity) -- Muzzleflash weapon.Effects.MuzzleFlash = CreatePointLight(3) weapon.Effects.MuzzleFlash:SetParent( weapon.ViewModel ) local muzzleflash_color = weapon_table.muzzleflash_color or Vec4(1,0.6,0.0,1.0) weapon.Effects.MuzzleFlash:SetColor(muzzleflash_color) weapon.Effects.MuzzleFlash:SetPosition( displayposition ) weapon.Effects.MuzzleFlash:SetShadowMode(0) weapon.Effects.MuzzleFlash:Hide() function weapon:Update() --[[-- Idle State if (self:IsIdle()) then if (self:IsZoomed()) then self:LoopAnimation("WEAPON_ZOOM_IDLE") else if (self.IsMoving()) then self:LoopAnimation("WEAPON_MOVE") else self:LoopAnimation("WEAPON_IDLE") end end -- Fire State elseif (self:IsFiring()) then -- Zoom Fire State if (self:IsZoomed()) then self:SetAnimating(true) self:PlayAnimation("WEAPON_ZOOM_FIRE") -- Normal Fire State else self:SetAnimating(true) self:PlayAnimation("WEAPON_FIRE") end if (not self:IsAnimating()) then self:SetState(WEAPON_IDLE) end -- No State? (Idle) else self:LoopAnimation("WEAPON_IDLE") end]]-- -- State Correction if (self:IsFiring() and not self.Animating) then self:SetState(WEAPON_IDLE) self:SetAnimation("WEAPON_IDLE") end self:PlayAnimation(self.CurrentAnimation) self.ViewModel:SetPosition(self.Offset:Copy()) self:UpdateSounds() end function weapon:Think() -- This can be used for AI, etc. Implement it if you want end -- Animation function weapon:AddAnimation(name,startframe,endframe,speedmodifier,oneshot) self.Animations[name] = {Name = name, StartFrame = startframe, EndFrame = endframe, Length = (endframe-startframe), Modifier = speedmodifier, OneShot = oneshot or false, Running = false} end --[[function weapon:LoopAnimation(animation) local time = ((AppTime() - self.AnimationStartTime) / 100.0) local currentanim = self.Animations[animation] self.CurrentAnimationFrame = ((time * currentanim.Modifier * HOST_TIMESCALE) % currentanim.Length) + currentanim.StartFrame if (self.CurrentAnimationFrame < currentanim.StartFrame) then self.CurrentAnimationFrame = currentanim.StartFrame end if (self.CurrentAnimationFrame > currentanim.EndFrame) then self.CurrentAnimationFrame = currentanim.EndFrame end self.ViewModel:Animate(self.CurrentAnimationFrame, self.AnimationBlending, 0, 1) end]] function weapon:SetAnimation(animation) self.AnimationStartTime = AppTime() self.CurrentAnimation = animation self.Animating = true end function weapon:PlayAnimation(animation) if (not self.Animating) then return end if (animation == nil or animation == "") then return end local time = (AppTime() - self.AnimationStartTime) / 100.0 local currentanim = self.Animations[animation] currentanim.Running = true if (not self.Animating) then currentanim.Running = false end self.CurrentAnimationFrame = (time * currentanim.Modifier * HOST_TIMESCALE) % currentanim.Length + currentanim.StartFrame if (self.CurrentAnimationFrame < currentanim.StartFrame) then self.CurrentAnimationFrame = currentanim.StartFrame end if (self.CurrentAnimationFrame > currentanim.EndFrame) then self.CurrentAnimationFrame = currentanim.EndFrame end self.ViewModel:Animate(self.CurrentAnimationFrame, self.AnimationBlending, 0, 1) if ((self.CurrentAnimationFrame > currentanim.EndFrame-0.5 and currentanim.OneShot) or currentanim.Running == false) then self.Animating = false currentanim.Running = false end end -- Sound function weapon:AddSound(name,soundfile,frame) local sound = LoadSound(soundfile) local sound_table = {Name = name, Frame = frame, Sound = sound, Source = CreateSource(sound)} local Weapon = self function sound_table:Update() self.Source:SetVolume(Weapon.Volume) self.Source:SetPitch(Weapon.Pitch) end table.insert(self.Sounds,sound_table) end function weapon:UpdateSounds() for k,v in pairs(self.Sounds) do if (v.Frame == self.CurrentFrame) then v.Source:Play() v:Update() end end end -- States function weapon:GetState() return self.State end function weapon:SetState(state) self.State = state end function weapon:IsIdle() return self.State == WEAPON_IDLE or self.State == WEAPON_ZOOM_IDLE end function weapon:IsFiring() return self.State == WEAPON_FIRE end function weapon:IsReloading() return self.State == WEAPON_RELOAD end function weapon:IsZoomed() return self.State == WEAPON_ZOOM_IDLE or self.State == WEAPON_ZOOM_FIRE or self.Zoomed end function weapon:IsEmpty() return self:IsPrimaryClipEmpty() end function weapon:IsAnimating() return self.Animating end function weapon:IsMoving() return ((KeyDown(KEY_W) - KeyDown(KEY_S)) ~= 0) or ((KeyDown(KEY_A) - KeyDown(KEY_D)) ~= 0) end function weapon:SetAnimating(anim) self.Animating = anim end -- Weapon Functionality -- Reload function weapon:Reload() if (not self:IsIdle()) then return end self:SetState(WEAPON_RELOAD) end -- Primary Fire Mode function weapon:GetPrimaryClip() return self.Primary.Clip end function weapon:GetPrimaryClipSize() return self.Primary.ClipSize end function weapon:GetPrimaryAmmo() return self.Primary.Ammo end function weapon:IsPrimaryClipEmpty() return self.Primary.Clip <= 0 end function weapon:CanPrimaryFire() return (AppTime() >= self:GetNextPrimaryFire()) and (self:IsIdle() or self.IsMoving()) and (not self:IsPrimaryClipEmpty()) end function weapon:SetNextPrimaryFire(time) self.Primary.NextFire = time end function weapon:GetNextPrimaryFire() return self.Primary.NextFire end function weapon:TakePrimaryAmmo(num) self.Primary.Clip = self.Primary.Clip - num end function weapon:PrimaryFire() if (not self:CanPrimaryFire()) then return end -- We can not perform a primary fire self:SetNextPrimaryFire(AppTime() + self.Primary.Delay) self:SetNextSecondaryFire(AppTime() + self.Secondary.Delay) self:SetState(WEAPON_FIRE) self:SetAnimation("WEAPON_FIRE") end -- Secondary Fire Mode function weapon:GetSecondaryClip() return self.Secondary.Clip end function weapon:GetSecondaryClipSize() return self.Secondary.ClipSize end function weapon:GetSecondaryAmmo() return self.Secondary.Ammo end function weapon:IsSecondaryClipEmpty() return self.Secondary.Clip <= 0 end function weapon:CanSecondaryFire() return (AppTime() >= self:GetNextSecondaryFire()) and (self:IsIdle()) and (not self:IsSecondaryClipEmpty()) end function weapon:SetNextSecondaryFire(time) self.Secondary.NextFire = time end function weapon:GetNextSecondaryFire() return self.Secondary.NextFire end function weapon:TakeSecondaryAmmo(num) self.Secondary.Clip = self.Secondary.Clip - num end function weapon:SecondaryFire() if (not self:CanSecondaryFire()) then return end -- We can not perform a secondary fire self:SetNextPrimaryFire(AppTime() + self.Primary.Delay) self:SetNextSecondaryFire(AppTime() + self.Secondary.Delay) end -- Weapons Table Hierarchy if lastweapon==nil then firstweapon=weapon lastweapon=weapon else lastweapon.next=weapon weapon.prev=lastweapon end weapons[weapon]=weapon function weapon:Free() weapons[self]=nil end -- All weapons created from CreateWeapon inherit the base functionality implemented in this file weapon.Base = weapon return weapon end
gold xix:
dofile("scripts/classes/weapon.lua") GoldXIX = { } GoldXIX_WeaponTable = { ViewModel = "abstract::HUD.gmf", Offset = Vec3(-0.01,-0.005,0.05), Scale = Vec3(0.04,0.04,0.04) } GoldXIX = CreateWeapon(GoldXIX_WeaponTable) -- Animations GoldXIX:AddAnimation("WEAPON_IDLE", 277.0, 302.0, 1.0) GoldXIX:AddAnimation("WEAPON_ZOOM_IDLE", 212.0, 238.0, 1.0) GoldXIX:AddAnimation("WEAPON_MOVE", 304.0, 326.0, 2.0) GoldXIX:AddAnimation("WEAPON_FIRE", 62.0, 74.0, 3.0, true) GoldXIX:AddAnimation("WEAPON_ZOOM_FIRE", 262.0, 274.0, 3.0, true) GoldXIX:SetAnimation("WEAPON_IDLE") GoldXIX:SetState(WEAPON_IDLE)
-
There is a refraction shader, except it causes your model to not be affected by light and must be rendered in the foreground world.
-
Fascinating how good it looks. Maybe some muzzle flash would perfect it? I notice in STALKER the pistol has a faint heat haze effect, but I don't think that is too important.
Is there still a way to do sprites in Leadwerks? Or maybe a simple one-shot emitter could do the trick
-
Here is an image with some generalized "important" bone placement. Feel free to use as many of the bones in the limit as you want to get good animation or whatever. The bones should be named in all caps, just for ease of code.
-
SendMessage is a native BMX method exposed to Lua for TEntity types and derivatives.
-
You can use that as well. I just prefer the OO approach when possible
-
If you give FRAPs a higher priority than your System Idle Process you get higher FPS. I run FRAPs on the 2nd two cores of my CPU only so it doesn't affect Leadwerks running in the first and Newton running in the 1st and possibly? second.
-
It will get fired no matter where you sent the message from.
SendEntityMessage in C/BMX, model:SendMessage() in Lua.
-
Some nice improvements:
- I switched from the velocity model I took from Josh's original implementation: nextpos = currentpos + velocity/60.0 to my own based on one-dimensional kinematics:
-- This code is in the bullet:Update() function called every frame (no FPS impact) -- HOST_TIMESCALE is 1.0 for normal, 0.5 for half time bullettime, etc. -- bullet.gravity.y = -9.81 (m/s) -- bullet.mass = 0.0062 (9mm FMJ in KG) [Thanks to knowledgegranted] local delta_time = (AppTime() - bullet.starttime)/1000.0 * HOST_TIMESCALE nextpos.x = bullet.origin.x + (bullet.original_velocity.x * delta_time) nextpos.y = bullet.origin.y + (bullet.original_velocity.y * delta_time) + (0.5*bullet.gravity.y*bullet.mass*delta_time*delta_time/60.0) nextpos.z = bullet.origin.z + (bullet.original_velocity.z * delta_time)
- Bullet time is now implemented via HOST_TIMESCALE. I basically multiply all of the following by it (or some function of it):
- Bullet Velocity
- Move/Strafe/Jump Velocity
- Sound Pitch
- Animation Speed
New video is up with a demo of bullet time (audio is stuff because my volume was too high )
Reminding everyone this is all debug, and I am aware of many of the things that need polished, like the empty chamber anim is off, muzzleflash comes too soon, locker door fail, etc.
-
This seems reasonable.
-
It may be messing up if that is a mesh and not a model. Try using it on the parent of mesh1. (Passing mesh1.parent or whatever to the TForm... functions)
-
File Name: Debug Callstack/Trace/Globals Library
File Submitter: TylerH
File Submitted: 09 Dec 2009
File Category: Lua Scripts
This is a simple add-on library I coded based on the Lua debug library and some example code in the Programming In Lua book.
It features:
- Trace
- Traceback
- PrintGlobals
- LogGlobals
Trace will run a trace from the point in code debug.Trace() is called up to the top of the stack. This is basically a call stack, and produces output like so:
Trace: 1: Line 26 "Trace" Scripts/debug.lua 2: Line 40 "GetImpactDecal" scripts/classes/bullet.lua 3: Line 151 "UpdateBullets" scripts/classes/bullet.lua 4: Line 388 "N/A" [string "dofile("Scripts/constants/collision_const.l..."]
It will even trace into C functions, area of code with no function (i.e. the "N/A" you see), and through multiple files (compiled bytecode or normal).
Traceback is simply Lua's default implementation, except I print it out and log it in AppLog.
PrintGlobals will print the key names of everything in the _G table, this includes all of the entities, Leadwerks functions, things you don't know of, etc.
LogGlobals does the same as print globals, but prints to the AppLog and not the Console.
To use this simply call dofile("Scripts/debug.lua") in any of your Lua files (be it entity, game, etc.)
It is rather useful for tracing where errors occur more quickly than Lua error messages do.
-
I assume you want it local relative the mesh1 entity?
-
Ah fantastic work TylerH! Thanks mate, I'll give it a crack tonight.
(May as well start using this rep system. +1 rep for you )
Thanks
-
Thanks for the comments everyone. An AVI-raped video is now up.
OR
One of the bugs you will notice is that the gun disappears. In fact, it disappears when it is aligned to any world axis exactly (X,Y,or Z).
I am currently searching to see why this is.
-
Very nice. Once you release the source, if you didn't mind, I would love to add Catmull-Rom and Quaternion support to it.
We could get a nice Cinematic toolkit together with this.
Great project Rick!
- 1
-
I will upload a screenshot from Model Viewer with the Gold XIX weapon I have been using. I will have bones and hierarchy visible, and Photoshop some labels to the "important" bones.
I am at school right now, it is 10 AM EST, I get home and can have the screenshot up around 4 - 4:30 PM EST.
-
Making this post probably alone was a lot of work already. If you get this to work, it would be outstanding! Do you need weapons of any kind? I would be happy to help you with that.
Sure, I can always use more weapons.
The only thing I would really ask is that you make a bone called "FIRESPOT" where the bullet should leave the muzzle. This library is oriented towards artists who want to get their weapons working quickly or anyone who just wants some fun guns working with lots of features.
-
I have animation working for both looped and one-shot animations in my Weaponwerks Library.
For looping:
frame = (AppTime()/100.0) % (endframe-startframe) + startframe
For one shot animations:
animstarttime = AppTime() -- Call this once when you first start the animation ... frame = ((AppTime()-animstarttime)/100.0) % (endframe-startframe) + startframe
Then simply call model:Animate(frame,blend,0,1)
In cases where you need to speed-up or slowdown the application, you multiply the value to the left of the % (modulus) sign by a speed factor. 1.0 plays at default (has no effect), 0.5 halves the speed, 2.0 doubles the speed, etc.
If you need to compare the current frame (which is a floating point number) to a given frame (which is an integer) (i.e. I do this to check for sounds I have set to play at certain animation frames), round it to a whole frame using either math.ceil or math.floor (or code up a function like I did to ceil if the decimal place is .5 or higher and floor is less than .5)
- 1
-
Right now I am using weapons done by Errant AI at the Game Creators.
He is the same guy who made the AK74 Josh uses and calls an "SMG". I like the way he sets up his models with a FIRESPOT bone, BULLET bone, MAGAZINE bone, SLIDE Bone, etc. making it easy to setup any of his guns for use.
The video was shot in FRAPs (2.16 GB AVI file), I converted it to 287 MB .mp4 with no loss of quality, stayed up for 2 hours to watch it upload on YouTube, then it failed because YouTube doesn't support the format. I will have to just compress the AVI itself and upload.
That also gives me time to port more of the functionality into its own classes instead of a bunch of if...then...elseif...elseif...else...end statements.
- 1
-
I believe Nilium said you can make some changes so the main program CAN use Lua tables...
-
Just curious:
Local position is global position if it has no parent?
Else local position is relative parent?
-
Thanks Josh.
- 1
-
I needed better sound support in the projects I am working on, it was piss easy to integrate FMOD, and it even boosted my FPS 3-4 in situations where I was using lots of sounds compared to Leadwerks with OpenAL.
[WIP] Lua Weaponwerks (Now with video!)
in Programming
Posted
There are obvious sacrifices between the approaches. The state based approach makes it piss easy to do things like align sound frames exactly with animation frames, fire bullets at exact frames, etc.
Not having states wouldn't make hardly any difference, and are kind of redundant since the weapon only really reacts based on the animation being set.
How else would you handle it?