Jump to content

Rick's Blog

  • entries
    65
  • comments
    96
  • views
    24,770

DoWait() working


Rick

3,890 views

 Share

So I was able to get DoWait() working. It's not 100% ideal because you have to piggy back off another entities Update() method because of the limitation of not being able to have any global values. If you would like to try it out I'll give the steps here.

 

1. Make a file under the /Scripts directory named cr.lua and paste this code in it:

tblWait = {}
waitID = 1

clsWait = {}
clsWait.__index = clsWait

function clsWait.create(tm, func)
local wait = {}
setmetatable(wait, clsWwait)

wait.interval = tm
wait.cr = func
wait.createTime = AppTime()

return wait
end

function Init()
-- clear the table
for i=1, table.getn(tblWait) do
	table.remove(tblWait, i)
end

SetGlobalNumber("game", 2)
end

function RunScript(func)
cr = coroutine.create(func)

coroutine.resume(cr, cr)
end

function DoWait(cr, interval)
a = clsWait.create(interval, cr)
tblWait[waitID] = a
waitID = waitID + 1

coroutine.yield()
end

function UpdateCR()
local tblDeleteWait = {}
local i = 1

for k, v in pairs(tblWait) do
	if(AppTime() - v.createTime > v.interval) then
		coroutine.resume(v.cr, v.cr)

		tblDeleteWait[i] = i
	end

	i = i + 1
end

for k, v in pairs(tblDeleteWait) do
	table.remove(tblDeleteWait, v)
end
end

 

2. Replace your fpscontroller.lua code with the below code: (note backup your file :D )

All I did was create 2 global numbers and changed how the loop ends. You can end it with an escape if you like

dofile("Scripts/constants/collision_const.lua")
dofile("Scripts/constants/engine_const.lua")
dofile("Scripts/LinkedList.lua")
dofile("Scripts/filesystem.lua")
dofile("Scripts/math.lua")
dofile("scripts/classes/bullet.lua")

fw=GetGlobalObject("framewerk")

if fw==nil then
Notify("Null framewerk object.",1)
return
end

--Variables
dx=0.0
dy=0.0
camerapitch=0.0
camerayaw=0.0
move=0.0
strafe=0.0

--Create a player controller
controller=CreateController(1.8,0.45,0.25,45)
controller:SetCollisionType(COLLISION_CHARACTER,0)
controller:SetPositionf(0,2,0,0)
controller:SetMass(10)

controller:SetPosition(fw.main.camera.position)
camerapitch=fw.main.camera.rotation.x
camerayaw=fw.main.camera.rotation.y
controller:Move(Vec3(0,-0.9,0))

local gunscale=0.6
local vwep = LoadMesh("abstract::vwep_hands.gmf")
LoadMesh("abstract::vwep_gun.gmf",vwep)
vwep:SetParent(fw.main.camera,0)
vwep:SetPosition(Vec3(-0.18*gunscale,-0.03*gunscale,0.37*gunscale),0)
vwep:SetScale(Vec3(0.04*gunscale,0.04*gunscale,0.04*gunscale))
local gundisplayposition = vwep:GetPosition()
sound_gunshot = LoadSound("abstract::gunshot.ogg")
source_gunshot = CreateSource(sound_gunshot)
source_gunshot:SetVolume(0.5)
vwep :SetShadowMode(0,1)
local displayposition=Vec3(-0.26/2.0,-0.03,0.19)
local muzzleflash = CreatePointLight(3)
muzzleflash:SetParent( vwep )
muzzleflash:SetColor(Vec4(1,0.6,0.0,1.0))
muzzleflash:SetPosition( displayposition )
muzzleflash:SetShadowMode(0)

HideMouse()
MoveMouse(GraphicsWidth()/2,GraphicsHeight()/2)
FlushKeys()
FlushMouse()

local pick
local camera = fw.main.camera
local remainingtime
local starttime=AppTime()
local gameduration=2--length of game in minutes
local gamemode=0

gunpos = vwep.position:Copy()

local smoothedgunswayamplitude=0.0
local smoothedgunswayspeed	=0.0
local guntime = 0.0
local recoil = 0.0
local lastfiretime=0.0
local smoothrecoil=0.0
local swaydamping=0.0
local smoothswaydamping=0.0
local lightsmoothing =0.0
local gunlight = 0.0

--Flashlight
flashlight = {}
flashlight.light = CreateSpotLight(8)
flashlight.light:Hide()
flashlight.sound_switch = LoadSound("abstract::switch.wav")
flashlight.state=0
flashlight.light:SetConeAngles(30,35)
flashlight.light:SetRotation(Vec3(5,0,0))
flashlight.light:SetShadowmapSize(512)
flashlight.light:Paint(LoadMaterial("abstract::flashlight.mat"))

function flashlight:SetState( state )
if state~=self.state then
	self.state=state
	if state==0 then
		self.light:Hide()
	else
		self.light:Show()
	end
	if self.sound_switch~=nil then
		self.sound_switch:Play()
	end
end
end


function ShootBullet( position, direction )
--	local speed=100.0
--	local pick = LinePick( position, Vec3(position.x+direction.x * speed) )
end


SetGlobalNumber("game", 1)
SetGlobalNumber("runGame", 1)


--main function
--while KeyHit(KEY_ESCAPE)==0 do
while GetGlobalNumber("runGame") == 1 do
jump=KeyHit(KEY_SPACE)*6.0
if controller:IsAirborne()==1 then jump=0 end

if KeyHit(KEY_ESCAPE) == 1 then
	SetGlobalNumber("runGame", 2)
end

local time = AppTime()/3200.0
local frame = time*(179.0-96.0)+96.0
frame=clamp( frame, 96, 179 )
vwep:Animate(96,1,0,1)


--Camera look
gx=round(GraphicsWidth()/2)
gy=round(GraphicsHeight()/2)
dx=Curve((MouseX()-gx)/4.0,dx,3.0/AppSpeed())
dy=Curve((MouseY()-gy)/4.0,dy,3.0/AppSpeed())
MoveMouse(gx,gy)
camerapitch=camerapitch+dy
camerayaw=camerayaw-dx
camerapitch=math.min(camerapitch,90)
camerapitch=math.max(camerapitch,-90)
fw.main.camera:SetRotationf(camerapitch,camerayaw,0,1)

movespeed=6
movesmoothing=10
if controller:IsAirborne()==1 then
	movesmoothing=200
end

--Player movement
move=Curve( (KeyDown(KEY_W)-KeyDown(KEY_S))*movespeed,move,movesmoothing)
strafe=Curve( (KeyDown(KEY_D)-KeyDown(KEY_A))*movespeed,strafe,movesmoothing)

--Use objects
if KeyHit(KEY_E)==1 then
	pick=CameraPick(camera,Vec3(GraphicsWidth()/2,GraphicsHeight()/2,2.0),0,0)
	if pick~=nil then
		repeat
			if pick.entity:GetClass()==ENTITY_MODEL then
				break
			end
			pick.entity=pick.entity.parent
		until pick.entity==nil
		if pick.entity~=nil then
			pick.entity:SendMessage("use",controller,0)
		end
	end
end

--Update controller
controller:Update(camerayaw,move,strafe,jump,40,10)

fw:Update()

if KeyHit(KEY_F)==1 then
	flashlight:SetState(1-flashlight.state)
end

--Position camera
camera:SetPositionf(controller.position.x,controller.position.y+0.8,controller.position.z,1)

time=AppTime()
gunfirefrequency=80
gunswayspeed=0.001*20.0
gunoffset = gunpos:Copy()
gunswayamplitude = 0.02
if KeyDown(KEY_W)==1 or KeyDown(KEY_D)==1 or KeyDown(KEY_A)==1 or KeyDown(KEY_S)==1 then
	gunswayamplitude = 0.03
	gunswayspeed=0.005*20.0
end
smoothedgunswayamplitude = Curve( gunswayamplitude, smoothedgunswayamplitude,8.0  )
smoothedgunswayspeed = Curve( gunswayspeed, smoothedgunswayspeed,8.0 )
if smoothrecoil<0.001 then
	guntime = guntime + AppSpeed() * smoothedgunswayspeed * math.max(0.0,1.0 - smoothswaydamping)
end
gunoffset.z = gunoffset.z - smoothrecoil * 0.05
--smoothedgunswayamplitude = smoothedgunswayamplitude * (1.0 - smoothswaydamping)
gunoffset.x = gunoffset.x + math.sin( guntime ) * smoothedgunswayamplitude * gunscale
gunoffset.y = gunoffset.y + (1.0-math.cos( guntime*2.0 )) * 0.005 * gunscale-- * math.min(1.0,1.0 - smoothswaydamping))
vwep:SetPosition( gunoffset )
recoil = recoil-0.1
swaydamping = math.max( swaydamping - 0.05, 0.0 )
recoil = math.max(recoil,0.0)
smoothrecoil=Curve(recoil,smoothrecoil,3.0)
smoothswaydamping = inc( swaydamping ,smoothswaydamping,0.01 )
gunlight = math.max( gunlight- 0.2, 0.0 )
lightsmoothing =gunlight-- Curve(gunlight,lightsmoothing,8.0)
muzzleflash:SetColor(Vec4(1.0*lightsmoothing,0.6*lightsmoothing,0.0,1.0))
if lightsmoothing <0.01 then
	muzzleflash:Hide()
end
if MouseDown(1)==1 then
	if AppTime()-lastfiretime>gunfirefrequency then
		recoil = 1.0
		lastfiretime=AppTime()+math.random(0,20)
		gunswayspeed=0.0
		gunlight = 1.0
		source_gunshot:Play()
		source_gunshot:SetPitch(1.0 + (math.random()-0.5)*0.05 )
		swaydamping = 1.0
		muzzleflash:Show()

		CreateBullet(vwep:GetPosition(1) , fw.main.camera.mat:K():Scale(300))

	end
end

UpdateBullets()

flashlight.light:SetPosition(fw.main.camera:GetPosition(1))
flashlight.light:SetRotationf( curveangle( fw.main.camera.rotation.x, flashlight.light.rotation.x, 3.0/AppSpeed() ), curveangle( fw.main.camera.rotation.y, flashlight.light.rotation.y, 3.0/AppSpeed() ) )
flashlight.light:Movef(-0.07,-0.04,0.02)

print("Testing")
fw:Render()
Flip(0)

end

controller:Free()
ShowMouse()

 

3. Open up the editor.

4. Create a terrain.

5. Drop the Monstertruck model class into your scene.

6. Open up the monsterstruck lua file from the editor and replace the code. Note, you may want to backup this file also.

 

dofile("scripts/base.lua")
dofile("scripts/cr.lua")



function Spawn(model)
local entity=base_Spawn(model)
local pivot
local suspensionlength=0.25
local springconstant=30.0
local springdamper=100.0

Print("Inside Spawn")
init = false
Init()

entity.vehicle=CreateVehicle(model)
entity.tire={}
entity.model.userdata = entity.vehicle

pivot=entity.model:FindChild("tireL1")
if pivot~=nil then
	tireradius=pivot.aabb.h/2.0
	entity.vehicle:AddTire(TFormPoint(pivot.position,pivot.parent,model),tireradius,suspensionlength,springconstant,springdamper)
	entity.tire[0]=LoadMesh("abstract::vehicle_monstertruck_tire_left.gmf")
	pivot:Hide()
end

pivot=entity.model:FindChild("tireL2")
if pivot~=nil then
	tireradius=pivot.aabb.h/2.0
	entity.vehicle:AddTire(TFormPoint(pivot.position,pivot.parent,model),tireradius,suspensionlength,springconstant,springdamper)
	entity.tire[1]=LoadMesh("abstract::vehicle_monstertruck_tire_left.gmf")
	pivot:Hide()
end

pivot=entity.model:FindChild("tireR1")
if pivot~=nil then
	tireradius=pivot.aabb.h/2.0
	entity.vehicle:AddTire(TFormPoint(pivot.position,pivot.parent,model),tireradius,suspensionlength,springconstant,springdamper)
	entity.tire[2]=LoadMesh("abstract::vehicle_monstertruck_tire_right.gmf")
	pivot:Hide()
end

pivot=entity.model:FindChild("tireR2")
if pivot~=nil then
	tireradius=pivot.aabb.h/2.0
	entity.vehicle:AddTire(TFormPoint(pivot.position,pivot.parent,model),tireradius,suspensionlength,springconstant,springdamper)
	entity.tire[3]=LoadMesh("abstract::vehicle_monstertruck_tire_right.gmf")
	pivot:Hide()
end

function entity:UpdateTires()
	for n=0,3 do
		if self.tire[n]~=nil then
			self.tire[n]:SetMatrix( self.vehicle:GetTireMatrix(n) )
		end
	end
end

entity.model:SetKey("collisiontype",COLLISION_PROP)
entity.model:SetKey("mass",1.0)
return entity
end

function Kill(model)
local entity
entity=entitytable[model]
if entity~=nil then
	if entity.tire[0]~=nil then entity.tire[0]:Free() end
	if entity.tire[1]~=nil then entity.tire[1]:Free() end
	if entity.tire[2]~=nil then entity.tire[2]:Free() end
	if entity.tire[3]~=nil then entity.tire[3]:Free() end
end
end

function MyScript(cr)
Print("Inside MyScript() before DoWait()")
DoWait(cr, 10000)	-- wait 5 seconds
Print("Inside MyScript() after DoWait()")
SetGlobalNumber("runGame", 2)	-- exit the game
end

function Update(model)
local model
local entity
for model,entity in pairs(entitytable) do
	entity:UpdateTires()
end

if(init == false) then
	if(GetGlobalNumber("game") == 1) then
		Print("Running MyScript")
		init = true
		RunScript(MyScript)
	end
end

UpdateCR()

--Print("Inside monster truck")
end

 

7. Run the game from the editor and wait 10 seconds. The game will automatically end and go back to the editor after 5 seconds.

 

 

So what should you be looking for? in the monstertruck lua file I include cr.lua. This exposes some method and creates some tables. In the monster truck lua file I added UpdateCR() inside the Update() method. This is what I mean by piggy backing off another entities Update() method. If we would be able to have global variables you wouldn't need to do this step. In monstertruck I create a sort of init method. It makes it so when running in the game mode and the first time it calls Update() it'll run a function that defines the sequence you want to do. In this case it runs MyScript(). This is where the magic happens. It calls DoWait() and passes in 10000 (in ms 10 seconds). This is where it gets complicated to understand how it really works. If you take it at face value without knowing anything about programming it's easy. It means wait for 10 seconds but continue to run the game. Don't block the game for 10 seconds, that would be worthless.

 

If you study cr.lua you can see how coroutines work. Basically I assigned MyScript() as a coroutine. This allows me to jump out of MyScript() at any time with a call to coroutine.yield() when that's called from inside MyScript(). So then the question is how would we get back inside MyScript()? The answer is inside DoWait() & UpdateCR(). DoWait() adds an entry to the a table that stores the coroutine created, how long to wait, and the started app time. Inside UpdateCR() it loops through all entries in this table to see if the interval time has past. When it does it uses the coroutine stored to start up the MyScript() function again at where it left off.

 

So you can see how you can expand this to other functions. You can do a DoSound() that comes back after the sound file is completed. You can do a DoMove() which could move 1 entity to another at a given step and only come back into your sequence function when it reached it's destination. These 3 functions alone open up the door to so many interactive options that are easy to script.

 Share

5 Comments


Recommended Comments

The movement technique sounds interesting. Maybe a more advanced physics version could be implemented as well.

 

You have made some good arguments for a single-state system. See my post in the Lua forum.

Link to comment

Yeah, I was thinking about having 2 different moving methods. One that is physics based and one that isn't. The physics based one could be error prone. A person would have to be careful if they allow other physics objects to interact with a physics object that is running a move script though because it may make the physics object doing the move never able to reach it's goal. It wouldn't be the worst thing in the world since the game would continue to run, but the script sequence would obviously never finish.

 

I saw your post about a single-lua state system and I'm interested in trying such a system.

 

I'm going to work on the DoSound() next because it's the easiest ;) Non physics based DoMove() might take a little more effort.

Link to comment

The above is bugged btw. I have the fix just didn't post it yet. I also have DoSound() working now too which is cool. Give it a sound file and it won't continue with the script until the sound is completed. Currently working on DoMove().

 

My goal to show all of these off is to create a scene where the camera starts at one place and moves from pivot to pivot around the map, stopping at some pivots to explain something via sound, and continuing on after the sound is complete.

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...