Various Tricks Involving Movement, Collision and Line of Sight I Devised Over the Years.
Greetings all, I only recently came across your sight and read some of the rather helpful tutorials, many of the general ideas and details of which I came up with myself over the years of playing with the various clickteam products. Each of these expects the reader to have a certain level of knowledge with the slightly more complex aspects of klik coding, such as static movement, collision detectors etc.
Part 1: Anti-aliased static movement
First of these I'll mention is the problem with aliasing when it comes to static movement. Now, this was the problem I had, I would have say, an active object which is meant to move to a target location, and it would have 2 alterable values representing his inertia in the X and Y axis, with an always loop moving it every step according to how much inertia he had.
Now I wanted this object to slow down as it moved until it ground to a halt (imagine having thrown something from a top down view, or a spaceship that just cut it's thrusters and was floating to a halt) and to do this I did it by setting it's inertia to it's current inertia*0.9. In other words, cutting 10% off it's current speed so it's deceleration looked smoothe and gradual. I found, however, it would have an L shaped motion, sorta like this:
Now the problem here is that by cutting 10% off I'm working with fractions and decimals, but I'm converting that into an integer (since pixels cant be divided by two etc.) integer being a whole number with no decimal, of course. Even real time angle and speed calculations fall pray to this, the problem being in simple terms,that as the object gets slow, it should start moving at, say 0.9 pixels (which rounds down to 0) then 0.8 (which rounds down to zero). So it hasn't move any pixels when it should have moved 1.7
For example, lets say we want to halve the distance traveled every frame and start at a speed of 10 pixels per second and want it to continue for say, 7 frames. What we would want would be:
10 + 5 + 2.5 + 1.25 + 0.625 + 0.3125 + 0.15624 = 19.84374 pixels moved
(though it would round down to 19, and very soon 20)
But what would happen is:
10+5+2+1+0+0+0= 18 pixels.
As you can see, it not only cut off the last 10% of the distance traveled, but the movement stopped after the first 4 frames. And since the X and Y inertia values are of different sizes, one reaches the 'less than 1' range much faster the other, and so in that picture example, the Y stops moving before the X, creating that Kinked motion.
Now, to fix this, we need to add decimals to the motion. To do this I have 2 more alterable values, one being XPos and the other YPos. These values are set at the beginning of the frame to the objects current X and Y *100, and all further changes to location are done instead to these two alterable values, but multiplied by a factor of 100. Then on every event loop, I set the objects actual X and Y to it's XPos and YPos *0.01. In other words returning back to integers ONLY for display, the underlying locations are still decimal (accurate to 2 decimal points due to it being multiplied by 100 I doubt you'll see any error at this scale) Resulting in a nice, smooth movement from start to finish.
OK, this one I thought up recently, basically, this is a method of cutting down the amount of position testers when doing custom collision detectors.
Most will know the technique, take an object and have several other objects surround it that start complaining when they collide with backgrounds or other objects, like so:
Now the problem with this is now, instead of one object, we have FIVE. Which if you are using this method in a loop so you have multiple actives with custom collision detection going can get quite expansive (10 enemies = 50 actives!). However, you can reduce this to just 2!
If instead you have simply 1 active for the collision detection you can solve this problem. But, the first problem that comes to mind is, how will it tell which direction the collision is from? (helps to know which way the active should bounce, no point it bouncing up when it hits a wall now is there?) To do this, we hold each of the frames from the 4 objects, in 4 different directions:
(object shown grayed out so you can see the locations of each detector)
so you would have something like:
+Always - set “Collision” direction to Right +If “Collision” is overlapping the background - Set Right collision to yes +If “Collision” is NOT overlapping the background - Set Right collision to no
+Always - set “Collision” direction to Down +If “Collision” is overlapping the background - Set Down collision to yes +If “Collision” is NOT overlapping the background - Set Down collision to no
And repeat for the other 2 directions and what ever other locations you want to test for collision
Now, what this does is flick through each direction and do a collision check all in a row, in one event loop, before it has a chance to draw the collision object, effectively doing the job of just one object.
BUT WAIT, THERE'S MORE!
If you are clever, you could even reduce it to just ONE object be having the collision object simply be an animation of the object you want to test. All you would need to do is note it's current animation, direction and animation speed (preferably in the objects own Avalues) before the collision test and restore them after. With this you would even be able to eliminate the need for a loop, as each object would simply act independently.
Part 3: Line of sight
This one, as far as I know, requires MMF2, unsure if it's Dev or not, I use Dev myself. It requires the angle and scaling events.
The classic method of line of sight is to have it so that on every frame, a loop is run, where each active will fire a detector projectile from the active, in a straight line to the target, testing locations at X pixel intervals along the path, and throwing an event when either it hits an obstacle (no line of sight) or the target(line of sight). Now, this I thought was pretty cumbersome. So this was my solution.
First off, take an object, make it 1 pixel by 1 pixel in size, color it blue.
Second, set it's hot spot to be on that very same singular pixel.
Third, use a fast loop to assign each of the enemy targets one of these pixels
(it's a good idea to use the same loop to have objects pass any relevant information back to the parent object during the same loop, such as if the object is obscured or not, I'll get back to this)
Next, (going to need the direction calculator for this one) do something like this:
+ always - Set Xscale of“LoSPixel” to Distance(LoSPixel, Target, quality 0) (Direction calculator event) - Set Angle of“LoSPixel” to Angle(LoSPixel, Target, quality 0) (Direction calculator event)
This will stretch and rotate the pixel into a line which points from the active, toward the target!
Now, what you get is this (an image of me using this method in a project im working on):
In this example I have the active at the bottom (a hovering bomb that chases the player, red to signify it's angry and chasing the player) The red circle is the test target that it tests line of sight with, the blue line is our stretched pixel. That grey box is an obstacle.
Now what you want is is a small event like so:
+ If “LoSPixel” is overlapping the background - Set Obscured to Yes + If “LoSPixel” is NOT overlapping the background - Set Obscured to No
This can be repeated for any other obstacle object you might have
Now I said earlier you should pass information gathered by children(in this case our LoSPixel thats following around our active object)to the parents (The active object being followed) during the loop, so in the loop I would have a:
- Set “Active”'s obscured to “LoSPixel”'s obscured.
Thus now the parent object knows whether there's an obstacle in the way or not! Vwallah, here's a pic of it working:
Now what you see it the target up top, the active below (now a calmed blue to show it's no longer pursuing the player) and our LoSPixel (which I set to be colored red when obscured, so I can debug and see if it's working), the obstacle in the way fouling the line of sight!
BUT! There is still one problem!
The angle action seems to have a fairly heavy load, especially when done at a long distance! And by many objects. And so it's good to put constraints. As such I use, for example:
+ If Distance between LoSPixel and Target is less than 250 - Set Xscale of“LoSPixel” to Distance(LoSPixel, Target, quality 0) <-Direction calculator event - Set Angle of“LoSPixel” to Angle(LoSPixel, Target, quality 0) <-Direction calculator event
Replacing the always command with a range check, thus not bothering with the resize or turning if the target is out of range anyway. Another example could be:
+ If Target is overlapping A Line of sight mask - etc.
Where you would, for example, have a cone in front of the active that represents it's field of view, only checking for LoS when the player crosses over it.
I found with this method I can rather comfortably have 50+ enemies on screen with accurate Line of sight detection with only a 5-10 frame slow down (running at a default 50fps), which is barely noticeable.
(With some tweaking from Pixelthief's article on writing good code found at http://www.create-games.com/article.asp?id=1937 , I can actually get 100 enemies on screen without it dropping below 40fps, all the while each enemy having accurate and active collision detection)
There is a down side to this however, with this method you cannot detect WHERE the line of sight is blocked, simply that it is blocked (and what's blocking it). Although, the usefulness of knowing where it is blocked is questionable.
That would certainly work, you would either need a different animation for each object, or simply have the basic directions and set to different relative X and Y coordinates from the objects hotspot.
That would be a bit more complex, but, with it you could basically do collision detection for every single object with 1 object that has only a single frame.
Update: Thanks for the inspiration, certainly is doable. A quick hack together and i have 1 collision detector working for multiple object types (or any arbitrary amount too)
If there's any interest for it I can make a small article explaining how I did it
Also it seems editing comments causes some strange formatting artifacts :/<br />Comment edited by MrMcFlurry on 8/4/2009<br /> Comment edited by OldManClayton on 8/4/2009
However,I like to have the player shoot objects in all directions(only adds up to like 50 extra objects)
then,if the censor shot hits an enemy,it moves a "last seen at" (one for each enemy) object at 0,0 from player,in which the enemy will move towards if it has lost sight of the player for a short amount of time.
I was wondering...do your enemy's suddenly go into calm mode after,say, you move behind a wall?
@Oko: Yes, they aren't meant to be particularly smart enemies. They have 3 states, calm(doing nothing), armed(charging up to shoot towards a location near the player) and attacking(currently charging), if the player is out of LoS inthe charging phase, it returns to idle, but if it's attacking it'll continue it's motion. They're meant to be similar to mobile mines, not intelligent entities.
But the LoS is accurate. It would be easy enough to have them just aim towards a stored location that constantly updates whenever the user is in view, but I have code that tells them specifically to stand down when the player ducks behind a wall. Effectively pinning the player down while others move in for in the kill.
Problem with shooting in all directions is that you lose accuracy at further ranges, well you get the same issue with mine, but on a much smaller scale since it's 360 directions, not just 50. And with a little inspiration from eternal man, I'm currently working on an engine that requires just 1 universal active for both collision detection and line of sight that is recycled and reused for every enemy in the frame
Oh, another trick i should addabout how i have 2AValuesdenotingthe objects location instead of simply using X and Y. If you have a scaling coefficient when setting position (like say set X position to XPos*0.01*scale) where scale is a value between 0-1, then you can actually implement an effective zooming out (By also changing the overall scale of each object, would require mmf2). Only problem is that all backgrounds would need to be actives too
@ codecannon: Yes it can, but you need to do some fiddly setting like, adding a floating point value to an AV so that MMF will start treating it like a floating point instead of an integer, and it has caused trouble for me in the past(had many times where I've divided a number and it's rounded to an integer =/), so I basically use that method instead to make it a bit easier on my head.
Ofc, either way works perfectly fine, just i find i never have issues with this method.
good point mcflurry,though you could trick the enemy's that you designed,(finding enough wall space to sneak behind them)...then that just adds an awesome element of gameplay,lol,you also could do it to my enemies,however,you have to be faster.
and I use a 640 by 480 game screen,so even if it is in-accurate it's not for long,as the player moves around,thus changing the point of firing for the better.
(also it takes up roughly 50 objects because it fires often then they get destroyed,i actually used 36 dir built in shooting for censors)
and I only used built-in shooting for the censors(as I only want 36 to be fired,otherwise i'm going to have lagging),the lethal bullets are custom.
I actually tried a method similar to yours a long time ago but expierienced much crashing.
(and just your trying for just one active?,your far better at this then I am,lol)
in the long run,your method probably IS better,but I'm content with mine.
MrMac: Glad I could give you some inspiration!
Using only one active for all detector related actions shouldn't be that complex in theory. It's just a matter of fastlooping through every unit and using spread value together with loopindex for the information transfer. Promise to update me if you get it working!
I got the 1 object collision detector working well enough by doing just that EE, I just need to Extend it into being a LoS detector too
Since i basically do it with a single pixel sized dot that sets it's position to the objects bounding box parameters (X left("Active") etc.) the code works pretty universally, though i might add a flag in there to use fixed AValue boundaries, will work better for animations. But since it's a single pixel, i can also use it to sort out LoS with my above method too.
Using this article I've managed to make a really really cool laser beam effect out of your obstacle detector code. Ive made it a 5x5 block of pixels instead of 1 pixel, and divided the scaling effect by 5. When quality is turned onto 1 rather than 0, you can do really nice animated colour morfing blended alpha channeled laser beams Comment edited by Rox Flame on 8/24/2009