Jump to content

TylerH

Members
  • Posts

    525
  • Joined

  • Last visited

Posts posted by TylerH

  1. 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?

  2. 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)
    

  3. 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.

  4. 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):
      1. Bullet Velocity
      2. Move/Strafe/Jump Velocity
      3. Sound Pitch
      4. Animation Speed

    New video is up with a demo of bullet time (audio is stuff because my volume was too high :D)

    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.

     

  5. 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.

     

    Click here to download this file

  6. 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.

  7. 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.

  8. 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)

    • Upvote 1
  9. 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.

    • Upvote 1
  10. 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.

×
×
  • Create New...