How do you make a level?


(Pharap) #41

I didn’t have chance to do anything yesterday.
I might have some time later to write something.
I’ll focus on the collision code first rather than the multiple enemies.

Did you make any progress with the array tutorials?


Also, will your player character be animated?
And do you have the original sprite image?


(Pharap) #42

Actually, now I think about it, if an enemy turns around after walking into a wall, then it can no longer chase the player at the same time.

The two things are conflicting - chasing the player might involve walking into a wall, and then turning around means the enemy is no longer chasing the player.

So should the enemy not chase the player?
Or should the enemy chase the player until it hits a wall and then stop chasing the player?


I have written the code for having multiple enemies, but I won’t post it yet.
I think it would be good to see if you can manage it on your own first, since it’s a relatively small change.


(JohnnydCoder) #43

@Pharap,

Yes, I think I did but my code is getting errors when I compile.

Yes, it will be animated. I have the original sprite image. Do you want me to send it to you?

The enemy should not chase the player.

Thanks for finding those tutorials!


(Pharap) #44

I could have a look if you’d like, I can probably explain what’s going wrong.

I should have mentioned that some of the examples won’t work on Arduboy because they require the C++ standard library and the C++ standard library isn’t available for Arduino boards.

If you don’t want to install an IDE but you want to test some of the examples,
then you can use an online compiler like this one:
https://rextester.com/l/cpp_online_compiler_gcc

I was planning to create a mask for it so it can be drawn properly with Sprites.

If you want me to make the mask then I’ll need the original image,
or you could have a go at doing that yourself.

Ok, that makes more sense.

I have that tutorial and many other useful documents stored in my ResourceCollection repo:


(JohnnydCoder) #45

[quote=“Pharap, post:44, topic:7258”]
I could have a look if you’d like, I can probably explain what’s going wrong.
[/quote] Here is the code:

Entity enemies[2];
  Entity::enemies[0].x = tileWidth * 7;
  Entity::enemies[1].x = tileWidth * 10;
  Entity::enemies[2].x = tileWidth * 12;

  Entity::enemies[0].y = tileWidth * 7;
  Entity::enemies[1].y = tileWidth * 7;
  Entity::enemies[2].y = tileWidth * 7;

...

void drawEnemy()
  {
    constexpr int16_t enemyDrawOffsetX = (halfTileWidth + (enemyWidth - tileWidth));
    constexpr int16_t enemyDrawOffsetY = (halfTileHeight + (enemyHeight - tileHeight));
  
    const int16_t x = ((this->enemy.x - enemyDrawOffsetX) - this->camera.x);
    const int16_t y = ((this->enemy.y - enemyDrawOffsetY) - this->camera.y);

    this->arduboy.fillRect(x, y, enemyWidth, enemyHeight, BLACK);
    this->arduboy.fillRect(enemies[0].x, enemies[0].y, enemyWidth, enemyHeight, BLACK);
    this->arduboy.fillRect(enemies[1].x, enemies[1].y, enemyWidth, enemyHeight, BLACK);
    this->arduboy.fillRect(enemies[2].x, enemies[2].y, enemyWidth, enemyHeight, BLACK);

The error I’m getting is 'enemies' in 'struct Entity' does not name a type.


(Pharap) #46

You’ve got three problems.

Firstly you don’t need to put Entity:: in front of enemies.
:: is the ‘scope resolution operator’, you only use it when you need to access something that’s inside a different ‘scope’, e.g. something inside a namespace.
For example:

namespace Stuff
{
	constexpr unsigned int value = 10;
}

void someFunction()
{
	// Must use 'Stuff::' because this is outside the 'Stuff' namespace
	unsigned int value = Stuff::value;
}

Secondly, Entity enemies[2]; gives you 2 enemies, not 3.
I.e. you would have an enemies[0] and an enemies[1], but not an enemies[2].
If you wanted three enemies, you’d have to declare the array as Entity enemies[3];.

Thirdly, you can only access the elements of an array within a function,
you can’t access them from the global scope or function scope.
To initialise an array of enemies you’d have to write one of the following:

// Option 1:
Entity enemies[3]
{
	{ tileWidth * 7, tileWidth * 7, 0, 0 },
	{ tileWidth * 10, tileWidth * 7, 0, 0 },
	{ tileWidth * 12, tileWidth * 7, 0, 0 },
};

// Option 2:
Entity enemies[3] =
{
	{ tileWidth * 7, tileWidth * 7, 0, 0 },
	{ tileWidth * 10, tileWidth * 7, 0, 0 },
	{ tileWidth * 12, tileWidth * 7, 0, 0 },
};

(If Entity had some constructors then there would be some other ways as well.)


The drawEnemy function will compile, but it won’t do what you want it to.

I suggest reading about for loops if you don’t already know about them.
https://www.learncpp.com/cpp-tutorial/57-for-statements/

If you can’t figure it out then I’ll show you the solution I chose.


(JohnnydCoder) #47

I don’t know if I can figure it out, but at least I tried. :grinning:

Could you show me your solution @Pharap?


(Pharap) #48

Essentially you keep all the code that handles a single enemy and modify it so that rather than working on this->enemy it works on a reference parameter Entity & enemy.
Then you have an array of enemies that you loop through with a for loop and operate on each enemy individually using the code that was previously operating on this->enemy.

So basically you’re doing exactly what you were before, but instead of just operating on this->enemy, you’re running the same code for every enemy in the enemies array.

Does this make sense?
Is there anything you don’t understand?
(E.g. what a ‘reference’ parameter is.)


I haven’t written the code for moving the enemies back and forth yet because I was waiting to see if you could figure out the array part.

I might have time to write that tomorrow.


By the way, is the main goal of this for you to learn how to make games or is the main goal to get your game up and running?


(JohnnydCoder) #49

Thanks @Pharap!

Could you tell me what a reference parameter is?

The main goal is to learn how to make games, not just to get it up and running.


(Pharap) #50

Ok, good to know.

I had been thinking about suggesting that maybe I should just do all the programming if you were just wanting to get the game finished, but if the learning is the main goal then we’ll just keep going like this for now.

Though if learning is the main goal, I’d suggest maybe trying to write a few simpler games/programs first.
Platformers aren’t as easy as they seem.

I’ll explain and write some programs that demonstrate how references behave.
(As usual the explanation’s a bit long because I tend to go into detail about things.)

Firstly, here’s a program to demonstrate reference parameters in a function:
(Please read the comments as well as running it.)

#include <Arduboy2.h>

// Pass by value, does not modify value
// Works by making a copy of the argument
void incorrectAdd5(int value)
{
	value += 5;
}

// Pass by reference, does modify value
// Works by directly referencing the argument
void correctAdd5(int & value)
{
	value += 5;
}

Arduboy2 arduboy;

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

void loop()
{
	if(!arduboy.nextFrame())
		return;
		
	arduboy.clear();
	
	int value = 10;
	
	arduboy.println(value);
	
	// Pass by value, does not modify value
	incorrectAdd5(value);

	// Prints 10 because value wasn't modified
	arduboy.println(value);
	
	// Pass by reference, does modify value
	correctAdd5(value);

	// Prints 16 because value wa properly modified
	arduboy.println(value);
	
	arduboy.display();
}

Basically, when you normally pass an argument to a function,
the function actually makes a copy of that value.
That means that if a function’s parameter is modified in any way within the function it doesn’t affect the original value.

However, if the parameter is a reference parameter,
instead of copying the value, the function actually references the original value,
which means that if the function modified the parameter it will affect the original value.

In the case of the platformer’s updateEnemy function,
it’s important that the update function can modify the entity passed to it,
hence why a reference parameter is needed.

In the case of drawEnemy, technically the argument doesn’t have to be a reference,
but sometimes a reference is cheaper because it avoids making a copy and copies can be expensive for larger values/objects.
In this case I didn’t check, so drawEnemy might have been fine with a non-reference parameter (i.e. drawEnemy(Entity enemy)).

While I’m mentioning drawEnemy I’d also like to mention const references.

A const reference is a reference that a function can’t modify.
It’s important to know when an object/variable will/won’t be modified,
and in general functions should avoid modifying their parameters (because it’s easier to understand code when functions don’t modify their parameters).

Thus const references are used when you don’t want to modify a parameter but you still want to avoid copying the object.
(E.g. drawEnemy would be a good candidate for using a const reference because it doensn’t (and shouldn’t) modify enemy. So really it should be drawEnemy(const Entity & enemy).)

As for when to use a const reference vs a non-reference parameter…
On a desktop the rule of thumb is:

  • For a fundamental type (int, uint8_t, bool etc.), pass by value
  • For a class/struct type, pass by const reference (unless to need to modify the argument)

(There’s actually another part to the rule involving things called ‘rvalue references’, but that’s more advanced stuff and they’re not very useful on Arduboy anyway.)

On the Arduboy I don’t think it makes much difference most of the time because it’s rare to be handling large objects so you don’t need to worry too much.

Edit:
I did end up testing drawEnemy with both a const reference and pass by value.
They used the same amount of progmem, which I suspect is because the function is being inlined, so there’s actually no parameter passing happening at all.
(The compiler is free to inline functions if they’re only used 1-3 times or they’re significantly small.)
I decided to change the code to use a const reference anyway though.


I did say programs (plural), so here’s the second program.
This time I’m demonstrating reference variables.
Basically it’s the same idea - making a reference to something rather than copying it.

#include <Arduboy2.h>

Arduboy2 arduboy;

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

void loop()
{
	if(!arduboy.nextFrame())
		return;
		
	arduboy.clear();
	
	int object = 12;
	
	// Makes a copy of 'object'
	int objectCopy = object;
	
	// Makes a reference to 'object'
	int & objectRef = object;
	
	arduboy.println(object);	
	arduboy.println(objectCopy);	
	arduboy.println(objectRef);
	
	object = 15;
	
	arduboy.println(object);
	
	// objectCopy hasn't changed because it's a separate value
	arduboy.println(objectCopy);	
	
	// But objectRef has changed because it's just referencing 'object'
	arduboy.println(objectRef);
	
	// Changing 'objectRef' actually changes 'object'
	objectRef = 20;
	
	// 'object' has been modified through 'objectRef'
	arduboy.println(object);
	
	// objectCopy hasn't changed because it's a separate value
	arduboy.println(objectCopy);	
	
	arduboy.println(objectRef);
	
	arduboy.display();
}

Hopefully it makes sense.
Like I say, the idea is exactly the same, except this time there’s no function involved, just variables.

Two last things to say about references:

  • A reference cannnot be ‘repointed’ to a different object - they are bound to precisely one object and refer to that object for the rest of their ‘lifetime’
  • A reference must always refer to a valid object, there is no ‘null reference’

For these two reasons, references are almost always a much better choice than pointers.
(There are some very specific cases where pointers can do things references can’t.)


Edit
I have got the enemies moving back and forth now,
but I’ll wait until you’ve understood references before I upload that.
You might also have some more questions before then.
(Questions are important for learning, you can never ask too many.)