Well then, before we get started, we want sit back and think about the project: what are going to be the main issues to solve, what methods are we going to use and so on.
Gravity:Varying gravity is going to be a major feature of the game. That in itself shouldn't be difficult; all we need is two variables: one for the direction of gravity, and one for the strength. These are probably going to be actor variables as different objects will have different gravity at any given time. Calculating this gravity is going to be a little more tricky, especially when we have objects that are unusual shapes - if all we had were circles it would be a bit simpler. For starters, we can just work with a manually-set constant gravity, until we sort out some of the other stuff.
Collision actors:Now because we are going to be running around and rotating and all sorts of stuff, this is going to make collisions a little irritating, as our actor will be changing shape. The solution: use one actor for an invisible 'collision box', and another one to be the actual graphic - the part we see. So our actual actor will be one uniform shape, and won't be affected by the different animations. Since we want it to have perfect rotational symmetry, and we want it to be able to move smoothly over everything, we'll make it a perfect cirlce - let's face it, theres a reason they make wheels that shape.
This may cause a little graphical problem as, while a circle has an equal proportion of height-to width, most people do not. So we may end up with either objects being able to go through the player
without hitting him, or being able to hit him
without it looking like they actually touched him. The solution: you just have to find a balance and draw the graphics to fit the circle as much as possible, and hope it doesn't look too absurd.
Collision direction:GE has a wonderful function PhysicalResponse, which will move colliding objects so they are just touching rather than intersecting, and affect their velocity to cause bouncing or sliding, thus calculating the
angle of the collision surface. Unfortunately, it does not tell us what this angle is, nor can we access the information necessary to calculate this for ourselves. This is annoying, as it is something we will be needing to use in our game. When we're on the ground we'll need to constantly check which direction the ground it is, how steep the slope is etc, and when we're in the air and collide with an object, we need to figure out whether it's a wall, a floor or a ceiling. So we're going to need to find a way to calculate this angle.
I ended up using two separate methods - one for when we're on the ground, figuring out the slope, and the other for when we're in the air and a collision occurs.
The latter involves creating an array of
sensor actors around us, each of which knows what direction it is, and constantly checks to see if it's colliding with anything. Then, by checking all our sensors, we can see which ones are touching an object and therefore in which directions there are objects, and by averaging the values, we end up with one angle telling us which direction the surface of the object is in. Hooray!
There is a problem with this method. If I stand in a corner next to a wall, both the sensors below me and next to me will be activated, telling my that I'm standing on a 45 degree slope. This is all very well and in some games this might be just what we need. But in our situation it is not. We want to realize that we've run into a wall, but still know we are standing on the ground.
So I discovered another method, based on an idea that was discussed here on the forums some time ago. This time we use only
two sensors, or "bumpers" as I am calling them - both underneath us but one more to the keft and one more to the right, somewhere around where our feet would be. Rather than tell us if they're touching anything, they just move themselves with a physical response. Then we can calculate the direction between them, which will be parallel with the ground surface. Then we can just move them back to their place - like they're on springs - so they are continually pushed into the ground and thus continually 'feeling' the direction of our surface. Because they are still underneath us, if we run into a sharp angle - like our wall situation before - we will hit the wall before the bumpers do, and thus we bounce off and treat it as a wall, not a 45 degree slope; but where the angle is
not so steep, and we just have a slope rather than a wall, we can run up it smoothly.
Ground state:There will be a situation where we want to know whether we're on the ground or not. This happens in all platform games. You need to know whether you can jump, and whether you should be able to run or whether you are floating through the air. There have been various methods proposed of solving this problem, but this is the one I have found to be most effective: we create a sensor actor. This time we want it to be just one pixel in thickness, and fit just underneath our player, so that when he's standing on the ground, the sensor is intersecting with the ground, and when he's
not on the ground, the sensor is not touching it, either. Then we just check if the sensor is colliding, and there we are.
Our situation is a little more complicated, however. Because the ground can be any direction, the sensor needs to ba able to be in any direction - that's another reason why we needed to calculate the angle of the ground surface. So our sensor is going to need to be animated with a rotation.
I started with an image that was a ring which fitted just nicely around the collision circle, cloned an array of them, used a canvas to draw dividing lines across them, use PrtScrn to copy it into GraphicGale, erased the unnecessary sections and then the dividing lines, and then saved it to create a 36 frame rotation animation. Works like a charm.
Stats:For our general object stats, I use the method that Fuzzy shared
here. If you want to understand how this works (which is
very useful), you should do some research on binary operators. But for now, I think Fuzzy described it well enough that you should be able to at least get started. So here's how I'm using it.
I have an actor variable called stats which actors can use to determine what they're colliding with, and thus how to respond - that way, one collision script can work for collisions with any actor, so when we want to make changes, we only have to do it in one place. The first
two bits of the stats variable I am using to store a value for traction - so instead of it being true or false, is can be 0, 1, 2 or 3. All solid objects should have this value set to something. This tells us how slippery the surface is - so normal ground would have 3, something a bit softer might have 2, and something really slippery like ice can have 1.
The third bit of the stats variable tells us if the object is solid. - IE: we can walk on it and jump off it, and we can't go through it. So the global code I have for declaring values is like this:
- Code: Select all
// these are different traction values
#define _Trac_low 1
#define _Trac_half 2
#define _Trac_full 3
#define _Traction 3
// use this to check object's traction
#define _Solid 4
// this is a solid object
After that there are all my other values, for other facts I might need to know in the game. But that's up to you.
So in my ground object's Create Actor script, I have
- Code: Select all
stats = _Trac_full | _Solid;
Animations:GE cannot rotate sprites. This means that all rotations must be done manually. If we want to just rotate a static sprite, we need a 36 frame (or less in some cases, such as for smaller things) animation for it. This means that if we want to rotate an
animated sprite, we must create a 36 frame animation
for each frame of the original animation. If we just have a simple animation with not many frames (such as a propeller spinning an an aeroplane) this isn't too bad. But in our case we have a character with the ability to run and jump around, plus perform several other actions, and all must be rotated, and all must be done
manually. Currently, my character graphic has
39 animations. Of those 39, there are
2 which are
not 36 frame rotations - and that's only because in my game, gravity is constant. And there are still more animations to make for the actions I haven't added yet. Making all the animations becomes very tedious, but the work pays off.
ControlsRotating the view isn't possible. Although it could be simulated, this would be a great pain, drawing animations would become even worse as even the ground objects would have to be rotated, and it would have detrimental effects on collisions. So we're not going to try it. This means if we happen to be standing on the bottom side of an object, it will look like we're on the bottom of it. This could make controls confusing. Does pressing right move to the player's right, or
my right? And if I'm on the side of the object, do I use left and right, or up and down? And what happens when I start on the top, but run around to the bottom?
In Mario Galaxy, In the 2d sections I think you press in the direction you mant to go, and Mario runs on that direction - so If I'm standing on the left side of the planet I can press the control stick up to run clockwise around the planet. As long as I hold the stick down in that direction, he keep running clockwise. After I let go of the stick, he stops, and the direction I have to press the stick is again dependant on the side of the planet he's on.
While we could probably achieve this, it could become quite complicated to create. Just making it so right is always clockwise and left is always anti-clockwise is a lot simpler, but it may be more confusing for the player. It's up to you.
Final notesFor the sensors (both the direction away and the ground sensor) getAllActorsInCollision is your friend.
The direction array should be parented to a separate actor which can do the direction calculations for you. This also allows you to easily move the whole array as a unit without having to move each individual sensor - which you'll want to do as you'll want to have it moving ahead of you, to make sure it updates
before you have the collision.
Also, rather than adjusting xvelocity and yvelocity, I've made two separate actor variable xVel and yVel, which I use for the same thing. This has a few advantages, one of which is with pausing.
For pausing I have a variable, paused (it's a char, and can be 1 or 0 - true or false, and possibly other values later on). All Draw Actor scripts start off with
- Code: Select all
if(!paused)
so that nothing happens if the game is paused.
Wrapping up:That should get you started. I'll post some more of the actual code and stuff later. Just let me know if you need more explanation.