How do you make a level?


(JohnnydCoder) #1

I’m trying to make a game with levels (like Super Mario Bros.) and I was wondering how I could make a level. I created a small map but I don’t know how I can make the player interact with tiles (for example, Mario jumps on top of a tile that’s higher that the normal ground level or he runs into a wall).

Here’s a gif of what I’ve done so far:
ArduboyRecording%20(3)

If anyone wants to see the code, feel free to ask. :slightly_smiling_face:

Thanks!


#2

It depends on how you want the collision to work. If you want to be able to walk under the ledges that would probably be more complicated. If you want all the dotted area to be non passable you could set up a way to draw collision boxes around those mounds.

This might help you point in the right direction.
http://www.team-arg.org/mybl-technical.html

Mystic balloon is a little more advance since it also determines what tiles to put where. You don’t necessarily need that part.


(Pharap) #3

Questions:

  • How are you currently storing the tiles?
  • Would your game allow walking under platforms?

Rough answer:

It’s actually not that much different from a top down tile game:

  • Treat the world as a 2D grid of tiles
  • Give each tile a flag to say whether or not it’s solid
    • Alternatively give each tile a type identifer (typically an enum class) and have a function for deciding which types of tile are solid
  • Before moving an entity, look at the position the entity is about to move to and only move if the tile at that position isn’t solid

If your game won’t have the ability to walk under platforms, you can just treat the world as a list of numbers representing terrain height.
Not as much fun, but it greatly simplifies the code and reduces the amount of memory needed.

(I can’t write demo code because it’s late, but if you still need help tomorrow I might have time to write a demo.)


(Holmes) #4

With Midnight Wild, I did the same thing.

The trick is to be able to check which tile is under your feet. When it is solid, you need to stop falling and return to a walking state. To check what tile is under you, you need to take the X and Y-coordinates of the character and then increase the Y by a few pixels so that you are checking right below the sprite. Then, divide those (X, Y) values by the size of the tiles you use. You ought to end up with smaller numbers that can be used in a 2D array of map data.

All games are different, so this process won’t work exactly in all cases, so you’ll have to figure out how to get this method to work for your code.


(Simon) #5

Don’t forget to add the character’s sprite height!


(JohnnydCoder) #6

Thanks for the ideas!

I am storing the tiles like this:

const unsigned char gameTiles[3][32] PROGMEM =
{
//Sky Tiles
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xbf, 0xbf, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
// Ground Tiles
{ 0xe8, 0xb4, 0xfa, 0xfd, 0xfa, 0xf4, 0xe8, 0x50, 0xd0, 0xe8, 0xf4, 0xfa, 0xfd, 0xfa, 0xb4, 0xe8,
0xff, 0xef, 0xff, 0xff, 0xfd, 0xff, 0xb7, 0xff, 0xff, 0xff, 0x7d, 0xff, 0xff, 0xef, 0xff, 0xfd },
//Under Ground Tiles
{ 0xff, 0xef, 0xff, 0xff, 0xfb, 0xff, 0xdf, 0xff, 0xff, 0xef, 0xff, 0xff, 0xfd, 0xff, 0xdf, 0xff,
0xff, 0xbf, 0xff, 0xfe, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xde, 0xff, 0xff, 0xfb, 0xff, 0xff },
};

Yes, it would allow for that.


(Simon) #7

You may need to elaborate on what those values mean.


(Pharap) #8

I was hoping for more detail.
As @filmote points out, we have no clue what these magic numbers mean.

That means you will need a full 2D array then.
(Which is what you already have.)

This is why I strongly advocate scoped enums.


I’ll try to find chance to write a quick demo.
Though if you post the code that you’ve already got then I could just modify that,
which would be quicker than writing from scratch.


(JohnnydCoder) #9

Here is the gif again:
ArduboyRecording%20(3)

These are the background tiles.

const unsigned char gameTiles[3][32] PROGMEM =
{
//Sky Tiles
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xbf, 0xbf, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },

These are the tiles that have the grass on them.(The ones with the dots and the downward pointy things)

// Ground Tiles
{ 0xe8, 0xb4, 0xfa, 0xfd, 0xfa, 0xf4, 0xe8, 0x50, 0xd0, 0xe8, 0xf4, 0xfa, 0xfd, 0xfa, 0xb4, 0xe8,
0xff, 0xef, 0xff, 0xff, 0xfd, 0xff, 0xb7, 0xff, 0xff, 0xff, 0x7d, 0xff, 0xff, 0xef, 0xff, 0xfd },

These are the tiles under the ones with the grass on them(the ones that have all the dots)

//Under Ground Tiles
{ 0xff, 0xef, 0xff, 0xff, 0xfb, 0xff, 0xdf, 0xff, 0xff, 0xef, 0xff, 0xff, 0xfd, 0xff, 0xdf, 0xff,
0xff, 0xbf, 0xff, 0xfe, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xde, 0xff, 0xff, 0xfb, 0xff, 0xff },
};

Hope it helps!

@Pharap, here is the code.

(Crazy title, isn’t it?)


(Pharap) #10

Ok, that makes much more sense.

Instead of showing how the world is structured (which is what we were interested in), you’ve been posting the actual images.
That’s why we were confused.

It’s the world data we were (or at least I was) interested in:

I’m going to finish writing what I started writing using the images from your code so it looks the same.


(Stephane C) #11

That looks a lot like what I did after going thru Crait tutorials and when I started working on Space Cab. And yes collision detection has been a pain to me. Fortunately Filmote and Pharap saved that game idea from the pile of unfinished projects.


(Pharap) #12

@Johnnydb, I promise I’ll try to find some time to get an example up and running.

I was hoping I was going to have time earlier, but I was helping someone with something (actually non-programming related for a change) and it took longer than anticipated.


(JohnnydCoder) #13

Thanks @Pharap!

Sorry about that little misunderstanding about the world map, though I understand what you mean now. :grinning:


(JohnnydCoder) #14

Just one question: is there any way to measure how long someone holds a button?

Thanks!


(Pharap) #15

You would have to count the time yourself in either frames or milliseconds.

Counting milliseconds could be done like this:

#include <Arduboy2.h>

Arduboy2 arduboy;

void setup()
{
	arduboy.begin();
}

unsigned long millisecondTarget = 0;

void loop()
{
	if(!arduboy.nextFrame())
		return;

	arduboy.pollButtons();
	
	if(arduboy.pressed(A_BUTTON))
	{
		unsigned long now = millis();
		if(millisecondTarget == 0)
		{
			// 1000ms = 1 second
			millisecondTarget = (now + 1000);
		}
		else if(millisecondTarget < now)
		{
			// A button has been held for 1 second
		}
	}
	else if(arduboy.released(A_BUTTON))
	{
		millisecondTarget = 0;
	}

	arduboy.clear();

	arduboy.display();
}

(I think I wrote a timer class once to make this a bit easier,
but I’ll have to track it down tomorrow.)


(Shawn) #16

To count button press time, just use a pin change interrupt on the pin to watch for rising and then falling edges, grab the start and stop values of millis() and then subtract them (you’ll have to check and account for overflow). No polling or blocking and it will be very accurate comparatively.


(Pharap) #17

I think that might be a bit beyond @Johnnydb’s current ability.

I’ve had my Arduboy for at least three years and even I’m not sure how to do that.


(Shawn) #18

Good point, I didn’t think about experience or current ability. I’m just so used to programming micros by dealing with low level registers and writing my own libraries I sometimes forget not everyone does things the way I do.


(Pharap) #19

@Johnnydb, I finally found time to write a working example, but…

  • It could do with a bit of a tidy up
  • The gravity and jumping are a bit hacky
    • This could be fixed by using fixed points
  • I’m not sure how much of it you’ll understand, I’m using some techniques you probably aren’t used to
  • The way it’s currently implemented results in ‘slippery corners’
    • This is because I’m using ‘cross collision’ (treating the entity as a cross/diamond rather than a square)
    • There’s a way to fix this, but it would probably even be more complicated

If there’s anything you don’t understand, just ask and I’ll explain.
I’ve used lots of comments so hopefully most of the code will already make sense.

If I have time tomorrow I’ll try to draw some diagrams showing how the cross/diamond collision works.
Basically it looks for collisions at the centrepoints of each edge of the entity’s bounding box rather than the corners (which is why you get the ‘smooth corner’ effect).


(JohnnydCoder) #20

Thanks! I’ll take a look at it!