This is the second time that i write all this. Why ?
Well i did yesterday, but then, while writing, i asked myself,
can i do this without sensors in the form of objects.
And the answer is yes. We can. Realizing this i closed it all out without posting.
But then, i hate to leave unfinished things behind. Thats my dads fault. He taught me. Finish what you started.
Now i have to add a post explaining how to do this all without sensors in the form of objects.
But i better finish this first. Its more *visual* and easier to explain, and easier to make the bridge to not using objects anyway.
So here goes. Pick up the last cap that i posted as start layout.
I skinned this layout down from another project that i worked on. I noticed i forgot to clean up some things. Like the global variables. Bring up the global variables, and clean all the present variables. I am sorry ?
Now explore the layout that i left you. There is a maze, and i gave some little secrets about that maze in previous post. All Members of the maze are part of the Family "Blue". You can see that in the properties. Event can call Objects by there Family name. Very handy.
There is the Ghost01, which has already a few private variables added.
There is the Octopus that we will move. Add 2 Private variables to it.
Xstep, number, 0
Ystep, number, 0
There are 4 objects: U, R, D and L.
Scale them to 12 by 12.
Change there collision methods to bounding
Add 4 image points to the Octopus. Instructions how to in a previous post.
Name them U, R, D and L. Use respectively the 8, 6, 2 and 1 keys on your numeric to positions the points.
Go to the events editor. First we snap the sensors to the Octopus. Giving the beast tentacles.
Add a System: Start of layout condition to the empty sheet. Thats easier now then later.
For me the events sheet looks weird without one. It marks for me the start of a sheet.
Add a system : always condition.
Add a sub condition system: always.
(as i explained earlier, the first always we will replace with a flow condition, once we build this module in a complete game))
add 4 actions to this sub event
U Set position to object 0ctopus 0 (image point "U")
R Set position to object 0ctopus 0 (image point "R")
D Set position to object 0ctopus 0 (image point "D")
L Set position to object 0ctopus 0 (image point "L")
As you can see. Although we dont use the sprites of the objects in the game, they will go invisible,
good thumbnails extracted from those sprites make the events very readable.
You can run this to check if you snapped up to up, right to right and so on.
Next event will move the Octopus.
Add another "always" sub to an "always" event to the sheet.
add 2 actions to the sub "always"
0ctopus Set X to .X + 0ctopus.Value('Xstep')
0ctopus Set Y to .Y + 0ctopus.Value('Ystep')
It is not moving yet, but it will do so soon.
There will be no key inputs. The Ghost01 has to move all by itself.
Add a new always condition.
First we do the Overlap detections, to sense if the Octopus can go in a certain direction ...
or that there is a wall sitting in the way.
We can call all the wall objects by the Family name "Blue"
We also need a way to memorize the results. In a next step we will partly randomly choose a direction, partly force a direction not blocked by a wall. I will use a String for that, and at same time introduce basic string operations.
Add a sub event containing an "always" to the last event.
First we will reset the string.
The action will be:
Ghost01 Set 'Possible_Ways' to ""
Possible_ways is a private variable carried by Ghost01. "" stands for an empty string.
Now lets find out all "Possible_ways" the Octopus can go.
If object U overlaps Blue, then that way (upwards) the Octopus can not go.
But we need the the directions that it CAN go. Just invert the Overlap Conditions.
Add a second sub event with the condition
U overlaps Blue (inverted)
Add an action to this
Ghost01 Set 'Possible_Ways' to Ghost01.Value('Possible_Ways') + "U"
(set value)
Copy this sub tree for R, D and L. And change objects in the conditions, and the strings in the actions.
U: overlaps Blue (inverted)
Ghost01Set 'Possible_Ways' to Ghost01.Value('Possible_Ways') + "U"
R: overlaps Blue (inverted)
Ghost01Set 'Possible_Ways' to Ghost01.Value('Possible_Ways') + "R"
D: overlaps Blue (inverted)
Ghost01Set 'Possible_Ways' to Ghost01.Value('Possible_Ways') + "D"
L: overlaps Blue (inverted)
Ghost01Set 'Possible_Ways' to Ghost01.Value('Possible_Ways') + "L"
What is all this doing ?
It resets the variable Ghost01.Value('Possible_Ways')
then it will add the letter "U" to the variable, which stands for upwards, if that direction is free from walls.
Same for R, D and L.
In other words, if there are no walls in the Left and Right directions, the variable Ghost01.Value('Possible_Ways') will hold the value "RL"
Note that you can, due the way we build the events structure, easily collapse every starting "always". Or invert it to keep it from running for debugging purpose. There is a "toggle event" in the ribbon, but for me inverting an "always" is easier, because there as contextual menu.
Al we have to do now is to decide which of the two possible directions we will go.
We will do this in a few conditions. But first lest make a 2 Global flow variables to guide those conditions.
Skip, numeric, 0
So_Many, numeric, 0
Make a new "always" event.
Add a sub "always' to it. Add an action to it to reset the variable Skip.
System: Always
System Set global variable 'Skip' to 0
Add another action to set the variable So_Many to the length of the variable Ghost01.Value('Possible_Ways')
System: Always
System Set global variable 'Skip' to 0
System Set global variable 'So_Many' to len(Ghost01 0 .Value('Possible_Ways'))
The length of 'Possible_Ways' is holding important info for us.
When the length = 1, then there is only 1 way to go, thats the way we have to let it go.
When the length = 2, and the way we are already going is still a possible way, then we will prefer to keep going the way we was going already.
When the length > 2, we will choose randomly, but with preferring the way it was going already, if this way is still a possible way.
This means that we also have to store the direction that we are going, before choosing another direction, in a variable. But that we do a little later in the events. Yet we will use it now.
This variable will be Current_way as private variable present in the Ghost01 object.
OK lets start this.
add a second sub event. Compare a global variable. Compare skip if its zero.
Add a second condition to this. Compare if So_many = 1
We store the actual way that we go in Ghost01's private variable Way_to_go
so the first action to this will be setting Way_to_go to Possible_Ways
the second action will tell the other conditions that we found a solutions. Its like a "dont bother, we know already".
There for we set Skip to 1.
This whole sub event looks like:
System: Is global variable 'Skip' Equal to 0
System: Is global variable 'So_Many' Equal to 1
Ghost01 Set 'Way_to_go' to Ghost01.Value('Possible_Ways')
System Set global variable 'Skip' to 1
You can copy this sub event tree, and just lazy edit the condition.
The comparing 'Skip' stays the same.
Comparing 'So_Many' will be against 2.
Add another condition to find out if the direction we are already moving is still possible.
To do this we will use the system expression "Find in string" put into the first value of a system condition "Compare", and compared as greater then the value 0
The sub event will look like:
System: Is global variable 'Skip' Equal to 0
System: Is global variable 'So_Many' Equal to 2
System: find( Ghost01 0 .Value('Possible_Ways'), Ghost01 1 .Value('Current_way')) Greater than 0
Find(string, string to find) , returns the position of the string we are looking in the string that we are researching. It will return a value higher then zero, if the string is found.
In our case, when the direction that we are going (Current_way) is present in the open ways string ('Possible_Ways'), then thats still the direction to move.
So the action will be, set the Way_we_go to Current_way,
and set skip to 1 because this is what we decided to do under those conditions.
the total event tree looks like this.
System: Is global variable 'Skip' Equal to 0
System: Is global variable 'So_Many' Equal to 2
System: find( Ghost01.Value('Possible_Ways'), Ghost01.Value('Current_way')) Greater than 0
Ghost01,Set 'Way_to_go' to Ghost01 0 .Value('Current_way')
System Set global variable 'Skip' to 1
You can copy this tree to lazy edit it.
the Skip condition stays.
So_many we compare against > 2
The find condition stays.
the action will add Current_way to Possible ways. In other words. This directions will be twice in the whole string. So when we randomly choose from the available directions , this direction has more chance to be chosen. At same time we update So_many to the new lenght.
Skip will not be changed.
Whole tree looks like.
System: Is global variable 'Skip' Equal to 0
System: Is global variable 'So_Many' Greater than 2
System: find( Ghost01.Value('Possible_Ways'), Ghost01.Value('Current_way')) Greater than 0
Ghost01Set 'Possible_Ways' to Ghost01.Value('Possible_Ways') + Ghost01.Value('Current_way')
SystemSet global variable 'So_Many' to len(Ghost01 0 .Value('Possible_Ways'))
You can copy this tree to lazy edit.
But all we keep in the conditions is the skip condition.
meaning, if there is no solution yet, lets randomly choose.
We will use the system expression "get middle substring"
as mid(String, Start, Count)
string in this = Possible_ways
start = random (the length) + 1 .. because random in construct starts from zero
Count = 1
mid( Ghost01.Value('Possible_Ways'),
random(global('So_Many')) + 1
,1)
this tree looks like:
System: Is global variable 'Skip' Equal to 0
Ghost01Set 'Way_to_go' to mid(Ghost01.Value('Possible_Ways'), random(global('So_Many')) + 1 ,1)
This is the final decision, so we will also set Current_Way to the same direction. Add a sub "always"
System: Always
Ghost01Set 'Current_way' to Ghost01 0 .Value('Way_to_go')
Now we covered all possible Direction changes. Our little Ghost starts to look smart.
Now all we have to do is translate U, R, D and L in to X and Y values.
Add a totally new "Always" event.
Add a sub event to this, compare the "Way_to_go" to the "U", and set the Xstep and Ystep variables according.
Add 3 sub events for the other directions.
This tree looks like:
System: Always
Ghost01: 24 Value 'Way_to_go' Equal to "U"
0ctopusSet 'Xstep' to 0
0ctopusSet 'Ystep' to -2
Ghost01: 24 Value 'Way_to_go' Equal to "R"
0ctopusSet 'Xstep' to 2
0ctopusSet 'Ystep' to 0
Ghost01: 24 Value 'Way_to_go' Equal to "D"
0ctopusSet 'Xstep' to 0
0ctopusSet 'Ystep' to 2
Ghost01: 24 Value 'Way_to_go' Equal to "L"
0ctopusSet 'Xstep' to -2
0ctopusSet 'Ystep' to 0
Now all we have to do is limit the Direction changes to the grid.
Locate the base "always" of the event that changes the directions. (should be line 12)
and change the always to a condition that checks if Octopus X is on the grid.
add a condition that checks if Octopus Y is on the grid.
Event will look like this:
System: 0ctopus 0 .X Equal to (round(0ctopus 1 .X /32)) * 32
System: 0ctopus 0 .Y Equal to (round(0ctopus 1 .Y /32)) * 32
The grid it has to move on is 32.
Round will round a number to the nearest integer, like 2,2 will be 2 and 2,7 will be 3
All points on the grid you can divide by 32 and the result will be an interger.
Now set the position of Gost01 to Octopus, up to you to choose where in the events.
The Whole events sheet looks Very simple when collapsed. Add some comments, and its very readable. Very Clean and logic too.
<img src="http://usera.imagecave.com/j0h/octupus/01limit.jpg">
Uncollapsed it looks.
<img src="http://usera.imagecave.com/j0h/octupus/02limit.jpg">
<img src="http://usera.imagecave.com/j0h/octupus/03limit.jpg">
<img src="http://usera.imagecave.com/j0h/octupus/04limit.jpg">
This method is very visual, very friendly to debug.
But i am sure not the most CPU friendly aproach.
Of course you will set the sensors invisible, and also mister Octopus.
But still.
Next post a small adjustment to do it without actual objects as sensors.