Scripts / A simple (hopefully) Lua tutorial to do something cool with GG.

Author
Message
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 12th Jun 2019 20:36
So this time I'm going to do something different, usually I plonk a whopping great Lua script on you all with a video showing what it does, or I try to explain how to use a whopping great Lua library full of goodness.

This time I am going to create a script from scratch, warts and all, and by scratch I mean a completely clean sheet.

As I will be making this up from scratch, I haven't even thought of something cool to do yet, I will try to explain each piece of code as I add it and at the end of each post leave you with a working script to play with.

So I'm going to go power up GG and think of something cool and useful to guide you through, brb.
Been there, done that, got all the T-Shirts!
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 12th Jun 2019 21:09 Edited at: 13th Jun 2019 21:07
Ok so I've thought of something cool(ish) which should cover quite a lot of the things that tend to keep cropping up in the scripting world.

Let's say we have several doors, standard animated doors from the GG assets, and we want to know whether the player has been through those doors, i.e. not just walked up and opened them but actually stepped though them.

Maybe we have an adventure game and we want to keep track of rooms visited or just use the fact that the player has entered a room to play a sound or something. Or maybe you need to know the order in which the rooms were visited?

So how do we go about doing something like that, well first, like I always do, we should take an existing script as a basic start point, then add our extra functionality … but … the existing scripts tend to suffer from some nasties that we need to clean up first, mainly things like global variables or poorly structured Lua which makes them hard to understand and hence hard to modify.

Now there is already a working door script called door.lua (amazingly enough) so we'll take that as a baseline and then step by step we can adapt it for our use.

So make a copy of the door.lua script and call it clever_door.lua.

Next let's make it a bit more readable by nesting it properly, this is the first attachment.
Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 12th Jun 2019 21:21 Edited at: 13th Jun 2019 21:08
Before doing anything else we attach our new script to a door and test it in GG … yep, it works as it should.

So now I will alter this script to be … um .. better. (the sharper amongst you may see I have already tweaked it a tad, but nothing drastic, just took out an if that wasn't doing anything and changed a global variable to be local).

So the first thing I notice about this script is that it uses g_Entity[e] in several places, now this is accessing a global list, which is not very efficient and as I know we will need to access this entities parameters from that list quite a bit the first thing I will do is change this to use a local variable instead (more efficient).

At the beginning of main I'll add the following:


Now everywhere we had 'g_Entity[e]' we can replace it with 'Ent', also the hard to read ['<variable>'] format can be replaced with '.<variable>' format.

This results in the second attachment.
Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 12th Jun 2019 21:32 Edited at: 13th Jun 2019 00:14
As before, test the new script! (I'll really hammer this home btw, get used to it!)

So what next? Well let's see if we can simplify the logic somewhat.

Well the next step is to make use of utillib.lua, to make this available to the script we need to add a 'require' call to the top of the script, this simply tells Lua that this script relies on functions from the specified library, so we add:


To the script and also, in order to let GG know that we need to include the library in the standalone, we add:


To the _init function.

Having done this we now have all the power of the functions in the utillib at our fingertips, this also means that unlike the global functions, which are inefficient, we access them as if they were local (I'm not going to explain why this is btw, just trust me on this) which is way faster.

The first function I'm going to use is PlayerCloserThan, this function take two arguments which is the entity id and the distance and simply returns 'true' if the player is within that distance and 'false' if not. We access this function by specifying the library (which in the require call we called U) and the function name like so:


This gives us …
Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 12th Jun 2019 22:13 Edited at: 13th Jun 2019 00:16
So what do you think we do next? Yup we test it! … and it works, cool.

Now I look back up the thread to see what I'm supposed to be doing, as at my age the memory is not so good, aha so we want to know for multiple doors (with this script attached of course) whether the player has gone through them.

Hmm, so we need to have a way of storing a status for multiple doors, i.e. a status of 'entered', which for now can just be a simple true or false, or iow a Boolean flag.

So let's add to our script such a list of status values.


Darn that looks simple, is that really all we need? Yep, as this script will be attached to many door entities we can initialise our list like this in the _init function:


This gives us a list of values, indexed (or 'keyed') by the entity id, of Boolean values called 'entered' which for each door will be initialised to 'false'. (btw, I may revisit this later as way more useful than a simple Boolean value would be a 'state' string which could be 'never opened', 'opened', 'entered' etc )

So now is the time to introduce a concept dear to my heart called 'debugging', in particular how to add sufficient 'logging' to a script that you can see what it is doing in real time and hence to be able to figure out when something goes awry.

We need to see the current 'state' for our doors, i.e. whether we have entered the door or not. Luckily GG gives us this ability via the PromptLocal function, this takes two arguments: the entity id and a string to display.

So we add the following to our _main function:


Now as it stands that would be fine for showing us when we have 'entered' the door but it may be more useful if it showed the opposite, i.e. if it displayed when we haven't entered! So let's change this slightly:


So this gives us ...
Been there, done that, got all the T-Shirts!
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 12th Jun 2019 22:40 Edited at: 13th Jun 2019 00:22
… and test … now is a good time to export the door and copy it around a bit so we have multiple doors to test. That way if there is any weird interaction between the scripts it should show up before we add any more code.

Now is also the time to figure out what 'entered' actually means for a given 'door' entity, and I admit fully that as I'm making this up as I go along I haven't actually given much thought to this yet, something will come to me I'm sure.

Hmm, so if the player is on one 'side' of the door and transitions to the 'other side' of the door then we need to set 'entered' to true.

A useful piece of info is given by the PromptLocal addition, I.,e. if you have tested this you will see that the location of the prompt in game is down at one corner of the door, so how can we use this to figure out what side of the door we are on?

Another hint is found by looking at the placement of the door in the editor, for the door I am using it is initially oriented west-east with the origin at the eastern edge, or to put this in game world coordinate terms the origin is at the +x edge (given an entity orientation of 0,0,0 which is the default when you place a new entity), btw +z is forwards so in this case if the player is on the -z side they haven't 'entered' the door, whereas if on the +z side they have.

Hopefully you are still following this, I know we are starting to inch frighteningly near to what GraPhiX calls 'funky AmenMoses math' but I will attempt to make it bearable.

In order for us to declare a door 'entered' we need to things two be true, 1) the door must have been 'opened' and 2) the player must be on the +z side of the door (in relation to the doors orientation - this will be clearer in a bit).

The first thing to deal with is whether the door has been opened or not (working on the principle that if the door has never been opened then the player cannot possibly have 'entered' it).

So examining the script, at the point where collision is turned off, i.e. in the if Ent.activated == 2 check, would be a good place to put something to indicate when a door has been 'opened'.

So let's add a second Boolean (for now) by simply adding:

In that portion of the code.

Also let's add a bit of logging:


This gives us
Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 13th Jun 2019 00:03 Edited at: 13th Jun 2019 00:21
So when we test this we see that each door shows us if it has been opened or not.

Next we need to work out what 'side' of the door the player is on, so first a word or two about entity orientation. GG uses Euler angles which are a horrible, painful, nasty, ugly .. well you get the idea that I don't like them but basically the x,y,z angles that specify the entity orientation in GG are relative to the game world not to the entity or player or any other frame of reference. This means that in order to do something useful with these angles we need to convert them to something more meaningful.

So how do we do this?

Well first we need to find out what the actual orientation of our door is in the game world, luckily there is a nifty way of getting this in addition to the actual position of the entty (remembering that for our door - or at least the one I've used - the 'position' is the bottom of the 'hinged' edge of the door) this is the function GetObjectPosAng, this function needs the object id which handily for us is given by g_Entity[ e ].obj or as we have simplified this a bit; Ent.obj.
The function returns six values, the first three are the position and the second three the Euler angles, i.e.:

This will give us in one function call the position and euler angles for our door although annoyingly the angles are in degrees rather than radians (although degrees are more human readable radians are way more friendly for math!).

So the next problem we have is where do we need to put this extra bit of code? Well we added the 'has it been opened' code in the part where the collision was turned off, and we then set the flag to indicate if the door had been opened so we can use the fact that this flag has been set to trigger extra math to work out which side of the door we are on:

So how does this help us? Well another useful function from utillib is the Rotate3D function, with this function you can specify a point rotated by Euler angles (in radians!) and get the resulting point in game world terms. For example lets say we want to work out a point that is 50 units 'behind' (remember I said forward is +z relative to the entity) our door, well we can do that by using the Rotate3D function (and a bit of math) .

So we have the actual (game world) position of the door, and we have the angles specifying the orientation for the door so we can calculate a position 'behind' the door, then all we have to do is work out if the player is close to the point the 'other side' of the door if so we must have gone 'through' the doorway!

(or at least that's the theory, practice will prove whether that is the case)

So the code looks like this:

This results in real world position 'behind' the door. So then all we have to do is work out if the player is closer to the 'behind' position than the '50'units, if so we must have 'entered' the door!

So now we use another utillib function, PlayerCloserThanPos, this function takes the x,y,z points of a position and a distance, it simply returns true if the player is within the specified distance of the position and false if not. As we know that our 'behind' points are 50 units 'behind' the door we can do the following:


So this gives us
Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 13th Jun 2019 00:06
Right so go test it!

Tomorrow, or when I can be arsed, I will take this a step further ... stay tuned.
Been there, done that, got all the T-Shirts!
PM
granada
Forum Support
22
Years of Service
User Offline
Joined: 27th Aug 2002
Location: United Kingdom
Posted: 13th Jun 2019 00:43
Great for non coders like me to start out with , thanks

Dave
Windows 10 Pro 64 bit
GeForce GTX 1050 Ti
AMD FX (tm)-9590 Eight-core Processor
31.96 GB RAM
1920x1080,60 Hz
PM
GraPhiX
Forum Support
19
Years of Service
User Offline
Joined: 15th Feb 2005
Playing:
Posted: 13th Jun 2019 08:33
Thank you AmenMoses this is brilliant my old brain hates lua but I am getting used to it now
Welcome to the real world!
Main PC - Windows 10 Pro x64 - Core i7-7700K @4.2GHz - 32GB DDR4 RAM - GeForce GTX 1060-6G 6GB - 1TB NVe SSD
Test PC - Windows 10 Pro x64 - G4400 @3.3GHz - 16GB DDR3 RAM - GeForce GTX 950 2GB - 500GB SSD
Laptop - Helios 300 Predator - i7 7700HQ - 32GB - Nvidia GTX1060 6GB - 525GB M2 - 500 SSD - 17.3" IPS LED Panel - Windows 10 Pro x64
Various Tutorials by me
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 13th Jun 2019 19:36 Edited at: 13th Jun 2019 20:15
OK so where were we, that darn old timers again … Ah yes so did everyone notice the intentional mistake? Course you did was pretty glaring, how could you possibly miss it!

If you cast your mind (or eye as it's just up there a bit) back to the design process I suggested that the algorithm was basically to detect if the player is 'behind' the door to then set a flag to indicate that the door had been 'entered', well this is true if we are approaching the door from the front but what if we are approaching from the other side?

Well, how about this for an idea; at the point at which we 'open' the door we could store an indication of which side of the door we are on and then trigger the 'entered' state when we are on the other side of the door!

Also I left the script with a few inefficient bits to see if anyone picked up on them, which of course you did and have probably already fixed them yourself.

So looking at the script where can we put this new 'which side of the door are we on' logic? Well we want to make sure we do this logic at the point the player presses the key to open the door but before the player has had a chance to move from that spot, so here:



i.e. the same place we added the code before, this is because the 'activated' value becomes 2 for the first time as soon as the E key is pressed to open the door.

Now we only need to do this code once and we need to then set some indication that we have done it plus store the 'side' of the door we are on somehow. Here is where Lua really comes into its own!



Now I introduce you to another technique I use a lot, rather than start hacking away at the script and adding more complex code which will be very similar to what we added elsewhere in the script I instead put a place-holder comment showing what I intend to add then I will save the script and give it another quick test. Then I step back and consider what this new code will look like and whether I can make it into a reusable standalone function.
Now we have to ask ourselves a few questions:
1) What do we want this function to calculate?
2) What does the function need as inputs?
3) Can we make any assumptions that may make the function easier to write?
The answers in this case are available by looking at the other code we added, if you recall that takes the object (the door) location and orientation and calculates a position for the 'back' of the door so we can then test if the player is 'close' to that position. So the answers are:
1) We want to find the position, 'front' or 'back' closest to the player using the same method.
2) Well as per the other code the function needs to know the object, the rest the function can derive for itself.
3) The assumption we must make in this case is that the door is not going to move! i.e. the orientation it has 'now' will be the same as later when we have to decide if we have 'entered' it. (I'll leave it as a mental exercise for you to work out what would happen if this assumption was not the case!)


So now we can make a stab at creating this magical function.

Take a look at the attached script and see how before I leap into the nitty gritty I add the function definition and place a call to it from the existing code, I also made the new function return a dummy value and altered the previous code we added to use that dummy value. Phew, so what do we do now? ... drum roll ... test it to make sure it still works (admittedly in the same flawed way iot did before!)
Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 13th Jun 2019 19:48
Before we add any new code I want you to take a look at what I added and make sure you understand how it works, for example this bit:

Note how even though we haven't actually added a 'side' value for our door yet we can still test it by name to see if it is there or not, i.e. 'nil' indicates that this value doesn't actually exist yet.
This is one of the most powerful things that Lua brings to the table, in any other language referencing a variable that hasn't actually been declared yet would result in a crash and burn situation, and in fact I'm sure you have all at some point had the dreaded 'nil' error reported from GG. This is because if a script tries to use a 'nil' value, for example by trying to print it or adding a number to it, then Lua will exit the script and report the fact back to GG.
So try to get into the habit of checking that a variable actually exists before trying to use it, especially if that variable happens to be a global variable from another script because you cannot guarantee that the other script has executed before your script!
A couple of neat tricks, I'll leave it to you to work out how they work:


Been there, done that, got all the T-Shirts!
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 13th Jun 2019 19:48 Edited at: 22nd Aug 2019 19:33
-- duplicate post --
Been there, done that, got all the T-Shirts!
PM
JPH-GAMES
3D Media Maker
9
Years of Service
User Offline
Joined: 13th Feb 2015
Location: Belgique
Posted: 13th Jun 2019 19:59
Very impressive sir, must take you a lot of time.
Thank you for that
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 13th Jun 2019 20:14 Edited at: 13th Jun 2019 21:23
Next I want to hammer home a few points:

1) test every change you make, no matter how small or innocuous it is because sooner or later you will add one too many 'small' changes and find you have a devil of a job working out which one is to blame for screwing up your script.
2) no matter what you change, always make sure you leave your script in a position where it can execute. There is nothing worse than coming back to a script after a weekend away (or longer) and trying to work out why it doesn't work!
3) before embarking on huge changes make a copy of the script that works! You will thank me for this tip more than any other and it's the one I find hardest to do myself!

Right so now lets make our new little function work properly, note how I have made it return a value which can then be fed into the existing logic, also note how I made this a named variable to remind me what it is for! Try to get in a habit of making 'magic numbers' in your scripts into named local variables in this way, not only does it make scripts easier to read but it means that at some point you can collect them together at the top of the script where it is easier to change them quickly to 'fine-tune' the way a script works.

So far we have used this script on a standard (well I have at least) sized door but what happens if we have a really big door? In theory with a named variable we can simply copy the script as 'big_clever_door.lua' tweak the value of that variable and bingo the script will work for the bigger door.

The idea here is that the function should return a negative value if we are 'in front' of the door when we open it and a positive value if we are behind the door, then when we check to see if we have 'entered' the door we simply negate this value in the check.


Note how similar this is to the code we already have to detect 'entering' the door.

We also need a tiny change to that original code:


Here we have negated the 'side' value because we are now detecting the 'other' side of the door.

Guess what we do now? We test the script! Note how now it doesn't matter which side of the door we are on when we open it we only see the 'entered' state change when we are on the other side.

Phew, next we'll do some more tidying up and fix those small inefficiencies I mentioned earlier.
Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 13th Jun 2019 21:59
Now examining the script we have so far we can see a lot of this: doors[ e ]

Now as the doors list is a local variable this isn't too bad, remember global variables bad, local variables good but we can do even better, how? Well unlike other languages when we make a local variable and assign to it a list entry we don't make a copy of it nut instead we sort of make a shortcut or pointer to it.
So if we say at the beginning of our _main function:



Then we can do two things, firstly we can replace all 'doors[ e ]' references with 'door' and we can test to see if we actually have a 'door' entry, if we don't have one then we should simply return without doing anything because this indicates that our '_init' function simply hasn't been executed yet.

Now you may ask why we should want to do this and the answer is that for entities that may be spawned dynamically we cannot be sure if our _init routines has been executed before the first call to our _main function, I don't (yet) understand why this sometimes happens but as it is so simple to guard against this happening I haven't put a huge amount of effort into finding the cause tbh.

Also we may have noticed that if we have already set the 'entered' state than we don't need to check for it so let's make a change to only set the state if not already set.


So let's make these changes and see what we end up with.
Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 13th Jun 2019 22:31 Edited at: 13th Jun 2019 22:31
And … test.

Umm, Interestingly I found a problem with testing.
Originally I just had doors hanging in space and testing went well but then I decided to put the doors into a wall to make it more realistic and suddenly found that the script no longer works.

So what to do next? How do we figure out why the door script has failed to work?
Well we need to add some more logging to our script to see where things are going wrong:


So now when I run the script I will get a prompt to show me if this bit of logic worked.

And retest ...

Yep the prompt shows so we know this part of the script is being reached.. so move the prompt to:

And re-test ...

Lo and behold, no prompt.
This means that in most likelihood 'plrvisible' is not being set to 1!

Why? Dunno, the GetEntityPlayerVisibility( e ) is supposed to set this value for us based on whether the entity (our door iow) is 'visible' to the player which is done using ray casting within the engine.
Now, full disclosure, I already suspected that this would fail at some point! Not sure why but the method used within the engine is flawed, probably due to the origin of the door not being central.
So is there something we can do to fix this ... yes of course there is otherwise I wouldn't have brought it up!
We can simply do away with the flawed engine mechanism and replace it with ... tada ... another utillib function!
In this case the function we need is PlayerLookingAtObj, this function takes several arguments, the object id, a distance, and an 'ignore' argument.
For this usage we only need the first two arguments as we are standing in front of the door and are presumably looking at it (and it's a big entity) so we can do the following:

etc

Which gives us
Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 13th Jun 2019 22:47 Edited at: 13th Jun 2019 22:47
And … test ... and umm it doesn't work because now we can't close the door …

Hmm, more debugging is in order.

Aha, so when the door is open the ray tracing check that the PlayerLookingAtObj function uses doesn't work, whereas the GG engine version obviously works (at least it does when the door is out in the open!).

Honestly this was entirely unplanned, we have fallen down one of those holes that GG sometimes (often times?) throws at you so I'm going to have to go think about this for a bit, brb.
Been there, done that, got all the T-Shirts!
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 13th Jun 2019 23:07 Edited at: 13th Jun 2019 23:24
Ok so a work around is to remove the checks on visibility for closing the doors, see the attached.

I've tested this and it appears to work ok, assuming we could see the door when within distance and we opened it then just checking the distance bit for closing it would seem sensible.

Maybe if someone can produce a proper level to give this a more thorough thrashing we can determine if this is an acceptable solution.

Anyhow, what is the next stage, well for that you'll need to wait for my next post.
Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 13th Jun 2019 23:48 Edited at: 13th Jun 2019 23:50
Ok so we have a script that works, in that the 'debug' prompts show that we are detecting when a door has been entered. So how do we now make this useful in a game scenario?

Well now we have several options, the one I'm going with is to have the name of the door entity added into the mix and then to add a global function to detect if a named door has been 'entered'.

The idea behind this is that a separate script can periodically call this function to then trigger further logic to do 'stuff', what that 'stuff' may be is left to the imagination of the game creator.

So first we need to store the name of the entity (our door iow), this is simple:
replace this:

with:


Note that now our 'door' will have a name, this name is editable in the editor via the Properties->Name setting.

So the first thing to do is change our logging to include the name:

and test ...
Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 14th Jun 2019 00:17 Edited at: 14th Jun 2019 00:18
So testing shows that all still works and the logging now shows the name of the door entity, now we can add a simple global function to detect if a particular named door has been 'entered'.

We'll name this function doorEntered and pass the name as an argument so we add this:


So this needs a little bit of explanation.

What this does is iterate through our 'doors' list and for each entry found return the entity id in 'k' (i.e. the 'key') and the actual entry in 'v'.
Then we can process each entry, in this case we detect if the entry name is equal to the one we are interested in and if so return the state of the 'entered' variable (which if you recall is either true or false.

If a matching door entry is not found then the default return value is 'nil' which is also treated as 'false'.

So now we can call this function to detect if a particular door has been 'entered' by the player.

Now to test this we could create a new script and attach it to a crate or something but there is a simpler way, remember we added some logging before to show the 'entered' state for this entity, well how about we simply alter that logging to use the new function? Like this in fact:



This gives us
Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 14th Jun 2019 00:21
So feel free to test this out,

In the next instalment we will add a bit of logic to detect when the doors have been entered in a particular set sequence based on the names of the doors, isn't that exciting!
Been there, done that, got all the T-Shirts!
PM
Pirate Myke
Forum Support
14
Years of Service
User Offline
Joined: 31st May 2010
Location: El Dorado, California
Posted: 14th Jun 2019 03:27
Well when you put it like this. It all makes sense.

Thank you for posting. Great toturial.
RIP
Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz, 2400 Mhz, 4 Core(s), 4 Logical Processor(s), 8gb RAM, Nvidia gtx660, Windows 7 Pro 64bit, Screen resolution 1680 x 1050.

New:
Intel(R) Core(TM) i5-8400 CPU @ 2.81GHz, 12GB RAM, Nvidia gtx1050ti 4gb, Windows 10 Home 64bit, Screen resolution 1920 x 1080. System Passmark 3774




AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 14th Jun 2019 14:13
One thing I haven't covered yet and now is as good a time as any, performance. Now as it stands our script will be executed every frame, so 60 times per second, so if we had ten doors all with this script attached then our code is effectively being executed 600 times a second!

Now thinking about this logically we don't really need to check 60 times per second for every door just to detect when the player is close enough to open the door! In fact 10 times a second should be plenty, so how to add logic to do this?

g_Time is a global variable that contains the number of milliseconds the game has been running, so if we somehow store a valkue representing now + 100 milliseconds and only execute the rest of the script when we reach that time we could effectively make sure that the major part of the script will only execute 10 times a second.

So let's add a 'timer' value to our doors list:


Notice how I have initialised this value to 0.
Next we need to add some logic to get the global time value and check whether 100 milliseconds has passed:


Examine this carefully and make sure you understand how it works, you may think that with the script only executing every 6th frame or so that the Prompt's would start to flicker or the functionality of the script may suffer but that isn't the case at all:

So next thing to do is make my little demo into more of an actual game, I have four doors which I've individually named as you can see in the video and we can now detect when the player has 'entered' each of the doors. Maybe the simplest thing to do next is add another script which we can attach to a crate and have that script report when the player has entered all the doors.

Our new script is really simple so I'll cover it in the next post.



Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 14th Jun 2019 14:45 Edited at: 14th Jun 2019 14:46
I've attached the script so now I'll describe what it does:

I have a list of the doors I am interested in, they are listed by name.

In the _main function we start off by making sure the doorEntered function is visible, remember what I said before because the scripts could be called in any order Luia may not have seen the global function we defined in the door script when this script is first executed so to avoid a dreaded 'nil' error we simply return without doing anything if the function is not available.

Next we initialise a flag to indicate when all doors have been entered, using a bit of reverse logic here we initialise it to true and set it to false when we find one of the named doors has not been entered.

Then we have a loop, notice how I am not using the key value so instead of 'for k, v in' I simply replace the 'k' with '_', this is the Lua way of saying 'don't care'. Now each time around the loop the 'v' variable will contain one of the entries from our list of doors, e.g. "Door1". ( a note of warning here, the pairs function does not neccessarily give you the items in the list in the order you expect! In this simple case it will as we have simply defined the list statically but in more complex situations like where a list is built up over time or constructed the items in the list could be in any old order and could even contain 'nil' values)

We check if v is valid (remember that 'nil' is also treated as 'false' so the 'if v' check will avoid us passing a nil value into the doorEntered function) and if it is we call the function to tell us if the door is entered, because I have said 'not' then if the door wasn't entered we execute the next line which simply sets our flag false.
Then, because we don't need to process any more of the list we have a 'break' instruction which tells Lua to quit out of the for loop.

Finally we use our flag to trigger a Prompt so we can see the result.

Here it is in action:
Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 14th Jun 2019 15:19
Now you have seen how to communicate between scripts, we could have multiple 'reporter' scripts each with a different list of doors and as many doors as we want all with the door script attached and everything works as we would expect it to.

As with the door script we don't really need to have the reporter script executing the loop every frame, in fact in this case we could probably get away with once per second so I'll tweak it to do that:


Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 14th Jun 2019 16:51
So what next, well I did mention checking if the doors are entered in a specific order so let's write a second reporter script to do that.

This script is a little bit more complicated than the previous one, basically if you go through the doors in the order specified in the list (and only in that order) then on going through the last door the script will report that all doors were entered in the correct order.

I'm running out of ideas now so how about for the next step one of you could suggest something along similar lines and I'll then implement it, let's stick with the doors but add some extra functions that would be useful in a game, maybe adding a function to count how many times the player 'enters' the door instead of a one shot deal, so for example the first time you enter the room you could trigger a message but the second time you can trigger a different message?

Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
granada
Forum Support
22
Years of Service
User Offline
Joined: 27th Aug 2002
Location: United Kingdom
Posted: 14th Jun 2019 23:04
Great tutorial sir, you are a teacher

Dave
Windows 10 Pro 64 bit
GeForce GTX 1050 Ti
AMD FX (tm)-9590 Eight-core Processor
31.96 GB RAM
1920x1080,60 Hz
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 15th Jun 2019 00:11
Btw, some may have figured out by now that if you use both reporter scripts you can detect if the player has been through every door and then check if they were visited in a specific order, so you could have maybe three different orders and change the behaviour of the game depending on which order the player chose. This is basically how you make a game that will play differently each time the player plays the game or another use could be to add 'Easter Eggs' that will only be triggered if a player does something in a specific order.

I would be interested in seeing what you guys (who are better at level design than me!) can come up with just using these scripts (obviously you need to replace the 'Prompt' parts with some code to actually do something).

In fact for homework I want anyone willing to do exactly that, if you need help in altering the scripts to do something just let me know what you want the 'something' to be and I'll show you how to add it.
Been there, done that, got all the T-Shirts!
PM
smallg
Community Leader
19
Years of Service
User Offline
Joined: 8th Dec 2005
Location:
Posted: 15th Jun 2019 10:27
just some ideas for anyone interested in taking up amen's proposal;
you could do a sort of PT maze where the player get teleported back to the start until he gets the combination correct or has seen the full story.
or a stanley parable type game with different narration/gameplay based on which door the player chooses
lua guide for GG
https://steamcommunity.com/sharedfiles/filedetails/?id=398177770
windows 10
i5 @4ghz, 8gb ram, AMD R9 200 series , directx 11
GubbyBlips
5
Years of Service
User Offline
Joined: 14th Jan 2019
Location:
Posted: 15th Jun 2019 16:20
Great ideas. Much appreciated detailed tutorial.
Will be looking into this closer. Linking scripts
will be a great benefit to know.
PM
Loretta
8
Years of Service
User Offline
Joined: 13th Aug 2016
Playing: Generation Zero; Skyrim SE , Fallout 4, In VR- Pistol Whip,
Posted: 15th Jun 2019 19:00
Amen Moses;
Thank you this is great!
Really love step by step tutorials!
Very appreciated!
ASUS ROG G20- Desktop Intel Core i7-6700 -3.4Ghz - 16GB Memory - GeForce GTX 1070 8GB - Windows 10 64bit
Alienware M15 Laptop i7-8750H, GTX 1070 Max Q
VR : Samsung Hmd Odyssey WMR and 2 Oculus Gos
PM
Pink Panther
14
Years of Service
User Offline
Joined: 1st Feb 2010
Location:
Posted: 15th Jun 2019 19:28
This is really great! I love learning this stuff and I really need the step by step at this point. As soon as I have a block of time I am going to work on this.
PM
GubbyBlips
5
Years of Service
User Offline
Joined: 14th Jan 2019
Location:
Posted: 16th Jun 2019 01:50
Okay, now I read the whole dissertation. (Doesn't mean I understand it!)
Lua seems to have a lot of little tricks up it's sleeve.
I don't think most lua websites show most of those tricks...?
Where do you find more instructional info? A book would be nice.
But this does cover many of the questions that are spinning around in
my head such as local vs global variables.
Again- I'll have to delve into it much more.

Very powerful little script. Only thing, when you start to shortcut the
variables n stuff-- that makes reading it more confusing (to me).
I'm not against it, I just need probably months! haha of looking over this
tutorial to really let it sink in.... yeah, I'm that thick!

Seeing as I don't like to copy and paste script lines without understanding
what they are doing, this will clarify some questions. So, thanks for this.
It will help, it's only my thick head if I can't figure it out.

I have a big script (another one) idea that hopefully this will help with.
I'll probably be back for help...!

PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 16th Jun 2019 07:01 Edited at: 16th Jun 2019 07:45
https://www.lua.org/manual/5.2/

https://www.lua.org/pil/contents.html

GG uses 5.2 so the everything in the first link applies when scripting for GG but the second link gives a more gentle introduction to the language.

If you want one of those old fashioned style books I can recommend the 4th edition of the book which is available on Amazon: https://www.amazon.com/exec/obidos/ASIN/8590379868/lua-pilindex-20

btw, this is what I consider light reading: https://www.lua.org/gems/sample.pdf
Been there, done that, got all the T-Shirts!
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 16th Jun 2019 09:06 Edited at: 16th Jun 2019 11:31
Whilst waiting for feedback here is the latest version of the script, I've changed it to use a state engine so you can see how much more readable the script is in this format, oh and something you may find surprising to learn is that although you would assume that the script should be less efficient with all those apparent string comparisons, it isn't because Lua stores strings only once and uses pointers to them at runtime, so in effect what looks like string comparisons are actually just pointer comparisons which in computational terms are actually faster than comparing numbers, the only thing quicker are Booleans.
Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 16th Jun 2019 11:33
Another version, I've added a count of how many times the door has been entered, see if you can work your way through the code to see how I've done it.

Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 16th Jun 2019 13:21
Remember when I said test, test and test again, well I found some bugs.

I've fixed them in this version and also made two small improvements; 1) I've randomised the door animation slightly, now it opens and closes quicker but also at slightly different rate for each door, 2) You can only now close a door if you are looking towards it, felt a bit wrong being able to close a door that was behind you!

Take a look at the changes and see if you can figure out what else I fixed, see if you can work out what the 'bug' was before those fixes were made.


Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 17th Jun 2019 01:39
Another change, discovered some animated doors that don't show up at all with ray-casting so changed the script to use the simpler "PlayerLookingNear" function. This script should work with all animated doors!
Been there, done that, got all the T-Shirts!

Attachments

Login to view attachments
PM
GubbyBlips
5
Years of Service
User Offline
Joined: 14th Jan 2019
Location:
Posted: 17th Jun 2019 05:44
Aha AM, those links reveal some of the tricks...
I promise to look into this script in detail, just some time will be necessary.
For some reason I spent about an hour fuming over a simple function script
(obj1 rotate to obj2) when all that was needed was for the scripted object
to be Isimmobile = yes. Didn't make sense to me, but that activated it!
PM
AmenMoses
GameGuru Master
8
Years of Service
User Offline
Joined: 20th Feb 2016
Location: Portsmouth, England
Posted: 17th Jun 2019 19:21 Edited at: 17th Jun 2019 19:28
To position or rotate an entity using one of the explicit rotation methods (there are a few!) you need to put CollisionOff(e) and CollisionOn(e) around the commands, like this:



Isimmobile doesn't actually make an object immobile btw, it is the way you tell GG that as an object isn't supposed to move it is safe to use polygon collision mode! Even with this flag set true you can still move it around via Lua but it is very computationally expensive to do so!

btw here is one I made earlier:
Been there, done that, got all the T-Shirts!
PM
GubbyBlips
5
Years of Service
User Offline
Joined: 14th Jan 2019
Location:
Posted: 17th Jun 2019 20:50
Well then...! Certainly I should have thought of that... haha.
Some days are better than others for scripting.
Now today I've been out in the sun (34 deg).
But will try to get something accomplished.
PM
Flatlander
GameGuru Master
17
Years of Service
User Offline
Joined: 22nd Jan 2007
Location: The Flatlands
Posted: 27th Jun 2019 06:17 Edited at: 27th Jun 2019 06:17
Just happened across this. Actually, I hadn't been in the "Script" forum lately and thought I'd stop by.

Just one word for this thread:

Brilliant!
Alienware Aurora R7 with SSD 256GB boot drive ( C: ) and a secondary drive ( D: ) that is 2TB
Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz 3.19 with Intel Turbo-burst
Installed RAM 16.0 GB
64-bit operating system, x64-based processor
Windows 10 Home
NVIDIA GeForce GTX 1070 with 8192 MB GDDR5 and 8095 MB shared system memory
Teabone
Forum Support
18
Years of Service
User Offline
Joined: 8th Jun 2006
Location: Earth
Posted: 17th Apr 2020 19:40
I just wanted to say, your functions work wonders!
Store Assets - Store Link

i7 -2600 CPU @ 3.40GHz - Windows 7 - 8GB RAM - Nivida GeForce GTX 960
Duncan_Idaho
5
Years of Service
User Offline
Joined: 29th Jul 2019
Location:
Posted: 18th Apr 2020 22:38
Thank you AmenMoses - this tutorial is awesome and the scripts you´ve deloped are very usefull
PM
GreekToMe
15
Years of Service
User Offline
Joined: 27th Oct 2009
Location: Colorado, USA
Posted: 13th Jul 2020 21:01
AmenMoses, I just discovered your tutorial. Thank you!
It is (for me) an eye opener in more creative and higher level scripting.
I will set this as a benchmark for my scripting adventures.
Now, if you ever get bored... , well, make another tutorial
I am sure there will be plenty of appreciative GG gamers.
Cheers!
Alexis A
PM
warlock12
8
Years of Service
User Offline
Joined: 12th Sep 2016
Location: Argentina
Posted: 17th Jul 2020 00:52
Only: Amen... A Big Zenzei!!!! Magistral...
The game is a serious thing (El juego es una cosa seria)

Login to post a reply

Server time is: 2024-12-22 03:05:12
Your offset time is: 2024-12-22 03:05:12