Jump to content

Josh

Staff
  • Content Count

    14,591
  • Joined

  • Last visited

Community Reputation

7,304 Excellent

About Josh

  • Rank
    Advanced Member

Profile Information

  • Gender
    Male
  • Location
    San Francisco, CA

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. @Rick that is EXACTLY the kind of thing I want to avoid, and this system does a nice job of it! However, you could do put all of one script's values in a special table if you wanted: function Script:HurtEnemy(amount) if self.enemy == nil then return end local healthMgr = self.enemy.HealthManager if healthMgr == nil then return end if healthMgr.TakeDamage == nil then return end healthMgr:TakeDamage(amount); end Or even simpler, just add a prefix to the function name that is unlikely to be used by another script: function Script:HurtEnemy(amount) if self.enemy == nil then return end if self.enemy.HealthManager_TakeDamage == nil then return end self.enemy.HealthManager_TakeDamage(amount); end But the default behavior is just going to call all occurrences of the TakeDamage function like this: function Script:HurtEnemy(amount) if self.enemy == nil then return end if self.enemy.TakeDamage == nil then return end self.enemy.TakeDamage(amount); end I hope to see some really neat emergent behavior come from this system when two scripts work together that weren't written with the other in mind.
  2. You will also get a set of very easy to use command to get and set values in Lua from C++. There are all part of the Entity class: void Entity::SetValue(const std::string& name, shared_ptr<Object> o); void Entity::SetValue(const std::string& name, const std::string& s); void Entity::SetValue(const std::string& name, const bool b); void Entity::SetValue(const std::string& name, const float f); std::string Entity::GetValueType(const std::string& name); std::string Entity::GetValueString(const std::string& name); bool Entity::GetValueBool(const std::string& name); float Entity::GetValueNumber(const std::string& name); shared_ptr<Object> Entity::GetValueObject(const std::string& name); An entity does not need to have any script attached to it to have values added, so you can use these to attach values to your entities even if you aren't using Lua!
  3. During development of Leadwerks Game Engine, there was some debate on whether we should allow multiple scripts per entity or just associate a single script with an entity. My first iteration of the scripting system actually used multiple scripts, but after using it to develop the Darkness Awaits example I saw a lot of problems with this. Each script used a different classname to store its variables and functions in, so you ended up with code like this: function Script:HurtEnemy(amount) if self.enemy ~= nil then if self.enemy.healthmanager ~= nil then if type(self.enemy.healthmanager.TakeDamage)=="function" then self.enemy.healthmanager.TakeDamage(amount) end end end end I felt this hurt script interoperability because you had to have a bunch of prefixes like healthmanager, ammomanager, etc. I settled on using a single script, which I still feel was the better choice between these two options: function Script:HurtEnemy(amount) if self.enemy ~= nil then if type(self.enemy.TakeDamage)=="function" then self.enemy.TakeDamage(amount) end end end Scripting in Turbo Game Engine is a bit different. First of all, all values and functions are attached to the entity itself, so there is no "script" table. When you access the "self" variable in a script function you are using the entity object itself. Here is a simple script that makes an entity spin around its Y axis: function Entity:Update() self:Turn(0,0.1,0) end Through some magic that is only possible due to the extreme flexibility of Lua, I have managed to devise a system for multiple script attachments that makes sense. There is no "component" or "script" objects itself, adding a script to an entity just executes some code that attached values and functions to an entity. Adding a script to an entity can be done in C++ as follows: model->AttachScript("Scripts/Objects/spin.lua"); Or in Lua itself: model:AttachScript("Scripts/Objects/spin.lua"); Note there is no concept of "removing" a script, because a script just executes a bit of code that adds values and functions to the entity. Let's say we have two scripts named "makeHealth100 and "makeHealth75". MakeHealth100.lua Entity.health=100 MakeHealth75.lua Entity.health=75 Now if you were to run the code below, which attaches the two scripts, the health value would first be set to 100, and then the second script would set the same value to 75, resulting in the number 75 being printed out: model->AttachScript("Scripts/Objects/MakeHealth100.lua"); model->AttachScript("Scripts/Objects/MakeHealth75.lua"); Print(entity->GetNumber("health")); Simple enough, right? The key point here is that with multiple scripts, variables are shared between scripts. If one scripts sets a variable to a value that conflicts with another script, the two scripts won't work as expected. However, it also means that two scripts can easily share values to work together and create new functionality, like this health regeneration script that could be added to work with any other scripts that treat the value "health" as a number. HealthRegen.lua Entity.healthregendelay = 1000 function Entity:Start() self.healthregenupdatetime = CurrentTime() end function Entity:Update() if self.health > 0 then if CurrentTime() - self.healthregenupdatetime > self.healthregendelay then self.health = self.health + 1 self.health = Min(self.health,100) end end end What about functions? Won't adding a script to an entity overwrite any functions it already has attached to it? If I treated functions the same way, then each entity could only have one function for each name, and there would be very little point in having multiple scripts! That's why I implemented a special system that copies any added functions into an internal table. If two functions with the same name are declared in two different scripts, they will both be copied into an internal table and executed. For example, you can add both scripts below to an entity to make it both spin and make the color pulse: Spin.lua function Entity:Update() self:Turn(0,0.1,0) end Pulse.lua function Entity:Update() local i = Sin(CurrentTime()) * 0.5 + 0.5 self:SetColor(i,i,i) end When the engine calls the Update() function, both copies of the function will be called, in the order they were added. But wait, there's more. The engine will add each function into an internal table, but it also creates a dummy function that iterates through the table and executes each copy of the function. This means when you call functions in Lua, the same multi-execution feature will be available. Let's consider a theoretical bullet script that causes damage when the bullet collides with something: function Entity:Collision(entity,position,normal,speed) if type(entity.TakeDamage) == "function" then entity:TakeDamage(20) end end If you have two (or more) different TakeDamage functions on different scripts attached to that entity, all of them would get called, in order. What if a function returns a value, like below?: function Entity:Update() if self.target ~= nil then if self.target:GetHealth() <= 0 then self.target = nil --stop chasing if dead end end end If multiple functions are attached that return values, then all the return values are returned. To grab multiple returned values, you can set up multiple variables like this: function foo() return 1,2,3 end a, b, c = foo() print(a) --1 print(b) --2 print(c) --3 But a more practical usage would be to create a table from the returned values like so: function foo() return 1,2,3 end t = { foo() } print(t[1]) --1 print(t[2]) --2 print(t[3]) --3 How could this be used? Let's say you had a script that was used to visually debug AI scripts. It did this by checking to see what an entity's target enemy was, by calling a GetTarget() function, and then creating a sprite and aligning it to make a line going from the AI entity to its target it was attacking: function Entity:UpdateDisplay() local target = self:GetTarget() self.sprite = CreateSprite() local p1 = self.entity:GetPosition() local p2 = target:GetPosition() self.sprite:SetPosition((p1 + p2) * 0.5) self.sprite:AlignToVector(p2 - p1) self.sprite:SetSize(0.1,(p2-p1):Length()) end Now let's imagine we had a tank with a main gun as well as an anti-aircraft gun that would ward off attacks from above, like this beauty I found on Turbosquid: Let's imagine we have two different scripts we attach to the tank. One handles AI for driving and shooting the main turret, while the other just manages the little machine gun. Both the scripts have a GetTarget() function, as the tank may be attacking two different enemies at once. We can easily modify our AI debugging script to handle multiple returned values as follows: function Entity:UpdateDisplay() local targets = { self:GetTarget() } --all returned values get put into a table for n,target in ipairs(targets) do local sprite = CreateSprite() self.sprites.insert(sprite) local p1 = self.entity:GetPosition() local p2 = target:GetPosition() sprite:SetPosition((p1 + p2) * 0.5) sprite:AlignToVector(p2 - p1) sprite:SetSize(0.1,(p2-p1):Length()) end end However, any scripts that are not set up to account for multiple returned values from a function will simply use the first returned value, and proceed as normal. This system supports both easy mix and match behavior with multiple scripts, but keeps the script code simple and easy to use. Scripts have easy interoperability by default, but if you want to make your function and variable names unique to the script it is easy to do so. Let me know if you have any other ideas for scripting in Turbo Game Engine.
  4. I just realized that with this system, any object could have a script attached to it now and it would work just fine.
  5. Josh

    Loading from the encrypted data file

    It is very weird when I search for something and find myself asking the same question years ago: https://www.gamedev.net/forums/topic/603062-minizip-encrypted-file-passwords/
  6. Josh

    Loading from the encrypted data file

    I do not know. It uses a crc table that is 256 elements in length to check if the password is valid.
  7. I think they must have made a D3D port for that.
  8. I'm just making some notes here. This is how to open VSCode with a specific file and line: And to also open a folder: To add a specific character in the line:
  9. Virtual reality rendering is very demanding on hardware for two reasons. First, the refresh rate of most VR headsets is 90 hz instead of the standard 60 hz refresh rate most computer monitors operate at. This means that rendering must complete in about 11 milliseconds instead of 16. Second, we have to render the scene twice with two different views, one for each eye. Without any special optimizations, this roughly means that we have to pack 16 milliseconds worth of rendering code into five milliseconds. There are a few optimizations we can make to improve efficiency over a naive implementation. Although VR rendering requires two different views to be rendered, the two views are only a few centimeters apart. We can perform culling for both views at once by simply widening the camera frustum a little bit so that both eyes are included the camera volume. For rendering, Nvidia provides an extension for single-pass stereo rendering. This doubles up the geometry and renders to two different viewports at once, ensuring that the engine makes the same number of draw calls when rendering in stereo or normal mode. (The same can be done using a geometry shader with Intel graphics, although it has yet to be determined if VR will be possible on this hardware.) Here is the result: Combined with all the other optimizations I've packed into Turbo, like clustered forward rendering and a multithreaded rendering architecture designed specifically for VR, this makes VR rendering blazingly fast. How fast is it? Well, it's so fast that SteamVR diagnostics thinks that it is going backwards in time. Take a look at the timer in the lower left corner here: The obvious conclusion is that we have successfully broken the speed of light barrier and time travel will be possible shortly. Start thinking about what items or information you want to gift your past self with now, so that you can be prepared when I start offering temporal tourism packages on this site.
  10. Josh

    Cant move or look around in scene

    Okay, do you have a high-precision mouse? This has been known to cause problems.
  11. Josh

    Cant move or look around in scene

    We have seen this problem before. I think you need to update your Nvidia driver.
  12. Solved a big problem with VR projection matrices working in Turbo. I have one good eye now!

  13. Josh

    Navmesh holes/collision erros

    It is possible the navmesh might be hidden a little below the terrain in that case. It is an approximation of the geometry so it doesn't always match the scene exactly. Fly into the terrain and see if there is anything under there in those spots.
  14. Josh

    Navmesh holes/collision erros

    That navmesh looks correct. The parts the terrain sticks out of are too steep to walk on. You can adjust the generation settings to reduce those long triangles, but other than that it looks right.
  15. Josh

    Upload bug

    You need the following file types: map lua tex mat mdl ttf wav ogg shader pfb phy And of course your executable (Waffles.exe). No other files need to be included.
×