Outrun-style 3D racing game WIP (with demo)

Now with android demo:

https://www.dropbox.com/s/c96yw105mdbpwsk/Sunset%20Racer%20-%20Dreams.apk?dl=0

By pure coincidence, while the awesome Matthew Pringle saw fit to revive his early graphics 2 demo, I was doing exactly the same, with a total re-write of my Outrun style game that I released a demo of when graphics 2 first came out.

It may not seem (visually at least) like progress on the old version (I’m working on getting traffic in now), the whole structure is drastically improved to allow for all the features you’d expect from a game of its ilk.

So here’s some demo vids. These are straight video-caps of the simulator using quicktime, so not ideal quality (although do set them to 720p 60 fps please!), but they show the features at least:

Themes (levels have themes, although not necessarily on a 1 per 1 basis, levels can share themes). These were seperated out with the idea of having an in-app level designer:

https://www.youtube.com/watch?v=yZGIVP70__M

For that extra retro feel, why not play it pixelated. Note this is not using a filter (which would slow it down), I simply draw everything shrunk into a small snapshot and then stretch the snapshot. Which means it is actually slightly quicker drawing it pixelated than at full resolution:

https://www.youtube.com/watch?v=n4H4jFgbQG0

And now for the real meat that the all-new code allows for. Firstly, I’ve got in level choices. Watch how on the first playthrough I go left of the trees in the middle and end up in the sunny level, while second time around I go right and end up in the snowy level. You also get to see how I (for now at least) handle the transitions between themes. These go very quickly as I recorded the game being played at 60 fps just to make everything quicker, but in reality the game will run at half the speed making the transitions last longer and be smoother.

https://www.youtube.com/watch?v=rqonVG0HF80

And also, I’ve abstracted out the concept of a player and his or her view from the game itself, meaning… multiplayer mode!

https://www.youtube.com/watch?v=_kO1iWW40Iw

Once I get traffic in, I’ll likely post a dropbox link so you android peeps can check it out and give feedback :slight_smile:

This is awesome Barry!!

Wow! Yes, this is amazing! So, are you planning to release the code for this at some point or just the videos and the apk?

Unlikely the source will be released unless you can convince Perk to buy it off me, but I’m happy answering questions on how I do things, so ask away!

Bear in mind the 3D part of it is actually the simplest (simply X and Y divided by distance at its most basic) - the majority of the work is in organising and processing the data.

I figured as much. I hope that you turn it into something really amazing so that I will at least be able to play it someday. I was more curious than anything else. I am not actually interested in 3D development at the moment since I don’t actually enjoy playing 3D games all that much. If you were to release your code, or any part of it, I would study it to see what pieces I could pull into a 2D game. All this to say that I don’t have any questions at the moment, but thank you for offering the answers! I think this looks amazing and it’s hard to put my mind around how to do something like this in Corona. Keep up the incredible work! :slight_smile:

Wow!  Awesome job @rakoonic!

So what’s next?  Please say “Space Harrier”.  :smiley:

Space harrier would be a great style to copy (or… Pay homage to :wink: ) owing to just needing a single touch for control. For some reason I was more thinking of Afterburner though, not sure why!

@jerejigga - to be honest there isn’t much of relevance for a 2d game, the core engine is very specifically for this sort of view. What sort of stuff are you looking for, there’s no reason to not start another thread here asking. I’m also a big fan of 2d stuff :wink:

Hi, I was wondering if you help a beginner out and could elaborate a bit on what you mean by

“Bear in mind the 3D part of it is actually the simplest (simply X and Y divided by distance at its most basic) - the majority of the work is in organising and processing the data.”

Say there is a 1x1 meter square, the closest edge is 2 meters from the bottom edge of the screen, and the farthest edge is one meter away. could you please give a quick explanation of how you’d create this perspective? Also (unless its a secret) would you mind cluing me in on how you handle camera rotation?

Well the first thing you need to do is break it down into basics. In 3D you don’t calculate squares or triangles directly, you calculate points. So for what you want, you’d calculate the position of the 4 corners of the square and use them to create the square.

I don’t tend to work from screen space back into world space, that is kinda doing things the wrong way around - you need to manipulate your ‘world’ (IE manipulate values in world space) and then convert to screen space, which is one way of saying taking 3D points and making them into 2D ones on your screen.

An easy-ish way of doing things would be:

  • Stick a group in your app so its origin is in the middle of the screen. This is going to be your reference point.

  • Work out your coordinates system. For now it might be best (although not necessarily the most wildly used) to assume in your 3D world positive X is to the right, positive Y is up and positive Z is *into* the screen (IE so the further something is in front of you, the larger Z value it has)

  • Now you just have to choose useful world points (IE a vector, or 3 element table with x, y and z values. You could do this with { 1, 2, 3 } or { x = 1, y = 2, z = 3} etc.

Now when I mean convert from world coordinates into screen coordinates, you’d essentially do this:

screenX = point.x / point.z * scaleFactor

screenY = -point.y / point.z * scaleFactor

Now you have the screen coordinates for whatever you want, when placed into your centered group. Two things to note:

  1. -point.y is because in screen coordinates, positive y is down, but in 3D we often say positive y is up, so I flip it.

  2. scaleFactor - this depends entirely on what you are showing, and you can tweak it to make things fit better on screen. It is roughly equivalent to field of view - the larger the scale factor value the lower the field of view and the bigger everything appears on screen.

There’s a few things to be careful of - the smaller the value of Z the closer something is and usually objects get very big. Also, never try to place any objects at z == 0 (ie level with the eye plane) as you’ll get divide by zero errors.

So for a square, I’d have 4 points. let’s say the corners are:

corner1 = { x = -1, y = 1, z = 4 }

corner2 = { x = -1, y = -1, z = 4 }

corner3 = { x = 1, y = -1, z = 4 }

corner4 = { x = 1, y = 1, z = 4 }

Simply pass each point through the above routine and then use these points (maybe stick a single sprite at each location, or draw a polygon connecting them, or use an image and the .path property) to draw something onscreen. To begin with I’d probably set up scaleFactor to be around a quarter of your screen width, that should at least make my randomly picked values above show something (probably).

Now, if you’ve managed to draw a square, and you want to rotate said square, you have to get into a bit of trigonometry, and this is where I can’t really help you with code off the top of my head, as it involves stuff like matrices, but:

What you’d want to do is make your square coordinates based around it’s own origin. So instead of the above you’d have:

corner1 = { x = -1, y = 1, z = 0 }

corner2 = { x = -1, y = -1, z = 0 }

corner3 = { x = 1, y = -1, z = 0 }

corner4 = { x = 1, y = 1, z = 0 }

Note how the Z is 0 in all cases. So how do we stop divide by zero errors? We invent the concept of a camera position. In this example we only need a Z offset, so to be equivalent to the first example, we’d say your camera is at Z = -4 (negative because the camera is *out* of the screen, IE in front of the square.

So now your calculations would become:

screenX = point.x / ( point.z - cameraZ ) * scaleFactor

screenY = -point.y / ( point.z - cameraZ ) * scaleFactor

And you’d end up with the same as above, but with one important difference - you can now manipulate your square and the distance to the camera will remain correct.

So how do you rotate your square? That’s the relatively tricky part. You’d use sin() and cos() to calculate the new X, Y and Z values of the four corners. In general you’d only alter 2 at a time, which is equivalent to rotating around one of the axes. For example if you want the square to rotate around the Y axis, you’d only be changing the X and Z values using sin() and cos(). I can’t go into them right now, but what I’d suggest is you start with just getting a simple square showing onscreen, then maybe try to move it around (adding offsets), and then search for simple 3D point rotation. I doubt I’ll guess correctly but they’ll look something like this:

– Rotate around the y axis

new.x = sin( yAngle ) * old.x + cos( yAngle ) * old.z

new.z = sin( yAngle ) *old.z - cos( yAngle ) * old.x

That is *not* correct, but it should give you an idea if your searching is giving you the right answer :slight_smile:

As for the turning in *my* game, it is very much hacked - in fact I can honestly say the game has no concept of corners directly - only offsets. This is why the 3D is only an approximation in these types of games :slight_smile:

Don’t know why this doesn’t have the high-res version, but all the same, here’s early stage of traffic:

https://www.youtube.com/watch?v=TC_hhIiRGRU&feature=youtu.be

Thank you!

EDIT: Also, by the way in your explanation you mentioned adding a camera offset of -4 to avoid divided by zero errors, would this just change where these errors occur from z=0 to z=4?

Android demo here:
https://www.dropbox.com/s/c96yw105mdbpwsk/Sunset%20Racer%20-%20Dreams.apk?dl=0

I cut down on the race option because I’m reworking some data structures for collision checking and am only updating a few races at a time, sorry :frowning:

Rakoonic,

I didn’t have a specific question before so I didn’t reply to your previous message that was directed at me. I read your response to Trakyan though and now I am intrigued further and have even come up with a use for this concept! You previously stated “there isn’t much of relevance for a 2d game”. I now disagree with that! :slight_smile: Your detailed post about using the 3d space and then doing the math to convert to 2d space was very insightful! I see at least two uses for this! One has to do with simple spinning objects like a door or ball. Something like that. The second use is much cooler though and has to do with adding depth to a 2d game. All the objects can effectively exist on a 2d plane (or set of 2d planes!) but by using this type of model, you can add really cool depth related effects to a game. My current game has no use for this except perhaps as a gimmick, but I will certainly keep your ideas in mind and this page bookmarked for future games.

Also, your demos look AWESOME! Fun stuff!

@Trakyan: I just saw your comment about the error changing from 0 to 4. The answer is a provisional ‘yes’. The simplest solution is you just move the camera are enough away from everything that nothing will ever cross the z == 0 plane.

If you have moving objects this may not be possible, in which case you’ll have to implement some kind of extended solution - the simplest of which is just to cull (IE not calculate or draw) any object that gets too close, eg if the distance from the camera Z value is < 1 (values are arbitrary and would change depending on your needs of course).

If you intend to start drawing walls / floors etc in 3D, then you are going to have some bigger problems - imagine you have a very big single square that defines the floor of your room - if you are in the middle of the room you aren’t going to want to cull the entire floor just because part of it is near and indeed behind you. In this case in Corona, your only practical solution is to subdivide, until this isn’t a problem.

Very nice.

To be honest it doesn’t *look* much advanced over the demo I had a few years back, but under the hood it has changed massively :slight_smile:
I’m starting to work on the actual ‘game’ elements now finally!

Hi, sorry for all the questions

I just noticed int he first video that there are slopes and hills which i didnt think was possible using a mode7 style effect, would you clue me in on how you did this?

@Trakyan - the simple answer is I’m not doing mode 7.

This game uses ‘real’ 3D to a certain degree - although there’s no rotating left or right, the track being drawn into the distance uses proper calculations.

Think of it like this:

As you can hopefully see from the videos, the track is broken down into short units - what I call a track section (or TS).

Each TS has the following properties:

  • What terrain ‘texture’ it has (actually a 1x1 pixel, obviously of a single colour)

  • What road texture it has

  • Scenery objects such as  trees etc - note these are only ever drawn at the front of a given TS for simplicity’s sake

  • Height value

  • ‘Cornering’ value

There’s some other stuff in there, such as collision data, offsets, lane info etc, but I’m concentrating on the pure visuals.

Now what I do is a two-pass system. First I calculate the 3D values, and then I use all those values to draw it.

The calculation goes something like this:

  • Loop from the camera position into the distance, until I reach my limit

  • For every TS I calculate it’s 3D values, based off the previous TS. These include screen positions, scales (used later on), themes etc.

To draw things I loop from the furthest distance to the nearest. Firstly, this means sprites are sorted correctly (IE things further away are drawn behind things nearer), but it also allows for some culling. Imagine if you are near the top of the hill - because I already know the screen Y position of the crest of the hill, it means I can choose to not draw anything that is further away and below that Y position. Every little bit of speed helps :slight_smile:

What I do for drawing generally is make all needed sprite textures as frames in a single animation. Then I have a pool of sprites (roughly 400 for the medium draw distance), then as needed I take a sprite, set its path values and frame (ok and alpha values so things fade out in the distance). And that’s ‘basically’ it.

Not sure if that was clear or not :) 

This is awesome Barry!!

Wow! Yes, this is amazing! So, are you planning to release the code for this at some point or just the videos and the apk?