-
Posts
7,936 -
Joined
-
Last visited
Content Type
Blogs
Forums
Store
Gallery
Videos
Downloads
Everything posted by Rick
-
I think the 3 statues as source are missing. I can't find them defined in lua anywhere. o SOURCE_STOPPED o SOURCE_PLAYING o SOURCE_PAUSED
-
What does the wav format need to be to be played in LE? I downloaded this danger.wav from http://www.moviesoundscentral.com/aceventura.htm but can't seem to get it to play in lua.
-
I think this would be a nightmare. I don't see a point in sending entity messages to move variables around when the variable should just be global.
-
That's possible also. Is there currently a way to know when a target is set, or would I need to poll in the Update() method to search for targets assigned to the entity? I was just thinking the volume trigger would have come properties like: CollideScriptFile = "mycollide.lua" CollisionOption1 -- these options could be static or maybe dynamic if possible Function = "MyCollide1" -- what function to run that is in mycollide.lua TookCamera On/Off -- this is what I was talking about. TookCamera would be the name of a switch you dragged into your scene. When you make it the target of this trigger it would dynamically add this property setting KilledGuard On/Off CollisionOption2 Function = "MyCollide2" TookCamera On/Off KilledGuard On/Off CollisionOption3 Function = "MyCollide3" TookCamera On/Off KilledGuard On/Off So what this is saying is when the player collides with this volume trigger, it would run different scripts based on the settings of some switches.
-
So to set a target you drag an entity to another. Does this trigger any sort of event in the receiving target entity or is there a way I can force the Spawn() method to be called? The reason I ask is because I'm playing around with a switch criteria to determine what scripts to play for a volume trigger. My idea is that this could be designed in the editor. Let's say I have 2 switches in my scene. I name them, "TookCamera" and "KilledGuard". I then would like to make these 2 targets of my volume trigger. The reason I want to know about this event inside my volume trigger is so that I can actually add a property radio button for the switch that gets added with the name of the switch. This way in the properties of the volume trigger I can say run this function on collision IF TookCamera is ON and KilledGuard is OFF and run this other function on collision if TookCamera is OFF and KilledGuard is ON etc. This would allow some pretty dynamic gameplay and all we would need to make it work is some kind of way of knowing when a target was added/removed.
-
I also have an event system that I've used a few years. The perk that this system has over most, not to take anything away from Pixel's system, is that it doesn't require your class methods to be static. You can run into scope issues with class data and static methods. I'll try to find it. The implementation is kind of complex, but the usage is very simple. Here would be an example of the usage: class Button { private: public: Event1<int> OnClick; }; class MyForm { private: void cmdExit_Click(int i) {} public: Button* cmdExit; MyForm() { cmdExit = new Button(); cmdExit->OnClick.Bind(this, MyForm::cmdExit_Click); } }; MyForm frm; // this will end up calling MyForm::cmdExit_Click() // the cool thing is that I have it setup so you can have a calling chain. I can any number // of different methods to the cmdExit->OnClick, and when I .Raise() the event all the // functions tied to it will be called frm.cmdExit->OnClick.Raise(5) One of the cool things also is that your function that handles your event can also be private to the class to prevent anyone from calling the event function directly.
-
I agree 100%. I actually like using lua. I just wish it had better class support. It's such a pain to simulate classes. If it had better syntax for classes/structures I would probably never use C/C++ again.
-
Here is the post on the old forum: Here is an example of how to get your own C++ code from a DLL for use in lua which can be called from the new editor. 1) Create a DLL project in VS. What you can do is create a new console app and during the wizard select the DLL option along with Empty Project. 2) In this project you'll want to add the lua include directory and the lua libs for linking. 3) Use this code as a test. Note that the prototypes of the one main method that loads the other methods should be inside extern "C". If it's not your function names will be mangled. The general ideas is that you expose 1 function that will load a table of all the other functions in your DLL. This makes it so you can access all the methods in lua. #define LUA_API_EXP __declspec(dllexport) extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" // prevents the function name from being mangled LUA_API_EXP int luaopen_test (lua_State *L); } #define PI (3.14159265358979323846) static int miles_to_km (lua_State *L) { double miles = luaL_checknumber (L, 1); double km = miles * 1.609; lua_pushnumber (L, km); return 1; /* one result */ } /* end of miles_to_km */ static int circle_calcs (lua_State *L) { double radius = luaL_checknumber (L, 1); double circumference = radius * 2 * PI; double area = PI * radius * radius; lua_pushnumber (L, circumference); lua_pushnumber (L, area); return 2; /* one result */ } /* end of miles_to_km */ static const luaL_reg testlib[] = { {"miles_to_km", miles_to_km}, {"circle_calcs", circle_calcs}, {NULL, NULL} }; /* ** Open test library */ LUA_API_EXP int luaopen_test (lua_State *L) { luaL_openlib(L, "test", testlib, 0); return 1; } 4. Now in lua, you load the DLL saying you want to start with the luaopen_test() method. Note that I made root a global variable that points to the directory of my game exe. So in this case the dll needs to be in the same directory as my exe. assert(package.loadlib(root.."Test.dll", "luaopen_test"))() -- if you don't use extern C your function name would end up looking something like --assert(package.loadlib(root.."Test.dll", "?luaopen_test@@YAHPAUlua_State@@@Z"))() I was able to get the mangled function name from dependency walker. A program that lets you see the inside of DLLs. 5. Now that the dll is loaded you can use the methods in it like so: print(test.circle_calcs(15)) So you might ask why you want to do this? Well, let's say you want to create some AI code that is speed sensitive. You don't want to do that in lua because it'll be way slower than if you did it in C++. What you want to do is the majority of the code in C++ and expose some functionality to act on the C++ to lua. So you create a DLL with all the C++ code to do the AI speed sensitive code. The other cool thing about this is that Josh had a vision of packaging models with lua scripts that act on those models. This fits right into that also. You can package your dll along with your model and lua script so others can use it. So basically your DLL will be a library that your lua script uses functions from to act on the models. Note: This is all going off the assumption that Josh hasn't already created a way for us to use our own C++ functions from within the editor.
-
That makes sense. I think once we are able to remove the need for us to use the main loop this would be useful. We just don't seem to be there yet. The main loop is still in lua files and we need to specify that loop. I agree that it would be nice to have the main loop inside the exe. In order for that to happen we would need ways to set all sorts of properties to config what we want to use and how to use it. A good start I think would be the camera. It would be nice if that was more module. Maybe it could have it's own lua file. That way you could just point to what lua camera file you want to use and it uses that style. That would allow the flexibility of people writing camera modules that people can just plug in because all FP or 3rd person cameras aren't all created equally for all game types. I think this would be better than creating a generic camera in the exe and only allow changing some settings because someone somewhere will need something totally unique that the settings exposed won't allow. So if it was just a lua file they could control it. I'm picturing treating the camera as another entity that you drag into the scene. You could then drag it to another entity to make the target association and set a lua file for the camera in the properties. Then the lua file would control how the camera works.
-
Made some bug fixes. I wasn't resetting everything correctly.
-
I'm sure I could find a use for it, but it seems like it's useful for addons where you don't have control over the main game loop, but we do have control over the main loop. That's why I have a hard time seeing where it fits in with LE.
-
It seems interesting. Where do you see this being used? Why would I use this as opposed to just calling the update methods by name?
-
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.
-
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 ) 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.
-
That's still little incentive for anyone wanting to get into the business as a game programmer knowing everyone uses C/C++. I'd love to program in VB for the same reason you stated, but tell a company you created a game using VB and they'll laugh at you.
-
I'm all for this, but is it really that simple? Won't you run into other issues with how lua scripts are currently used? entitytable for example will now be global and all entities loaded will be in it not just the entities local to one class model. Plus all scripts currently need to dofile() to base to use constants and stuff. That won't be needed because it'll only needed to be called once to load all that stuff. Like I said I'm all for it but I would assume there are more changes than what you stated. Maybe have some of us test out the new way and see how things work out.
-
I've noticed this as well.
-
One of the issues with BMax is that if the major company's used it to create games it might be taken more seriously. Until then it's C/C++. If there is no market for BMax in game studios then there isn't any incentive to learn it/use it.
-
The more I play around with ideas I think I can do this with targets and non globals. It won't be exactly how I envisioned but it should work about the same. Will get an example out as soon as I can.
-
It would be a script that gives a set of functions to allow these kind of interactions to happen. Think of these functions are the rules as to how things work. Writing a script for each interaction is the same as having to place all these triggers for each interaction. The same amount of work is put in. It's just different work. From what I've read scripting is a skill that mappers are required to know these days because it allows much more functionality. The script would be 1 function that flows how it would read: Do step 1 Do step 2 after step 1 Do step 3 after step 2 etc One of the features this opens up is that the script that runs the interaction can be dynamic. So you could setup 3 different sequence of events and randomly choose or choose based on other actions what gets ran. You just point to a different script or randomly choose one. I'll add that also, because I think that could lead to some very cool gameplay. The interactions wouldn't need to be hardcoded in the map. The supporting entities would need to be there but the script could use them differently depending on what script is ran.
-
The disadvantage is when you have to create a sequence of events that follow one after the other waiting until the one before it is complete, it starts making speghetti code with all the messages you are sending back and forth. And how specific and not general the messages are handled. The "...and so on" part you skipped is the part that starts waiting for things to be complete before continuing on. That's the hard part. Also, would you rather add a bunch of triggers to your scene or just use a simple script? I'll start working on this today because I think an example is needed.
-
Cool, that works. For things that I want ran every Update() call or on Collisions() for volume triggers I'll probably just do the separate filename/function thing for clarity.
-
I'll stress test it.
-
I'll make it easier. Here is one that I will be trying this weekend. You control in first person mode a player model When you collide with a trigger the following happens You lose control of the player The camera snaps to a pivot and points at the player model Music starts to play The player model moves to pivot2 Once there play a sound file of him talking Once that's complete move to pivot3 Once there play another sound file Once finished move camera back to fps and give back control I think I have an idea of how to make this work without globals and with targets. I'm going to try it your way I guess
-
Perfect! I'll create my own GetEntity() using CurrentWorld().entities and looking at a name property if one exists. That way the bugs will be on my plate and not yours.