Just got my Arduboy - What Game Should I Make? + Giving Away My Concept Images!

(Miloslav Číž) #21

Excel spreadsheet

Nice creative use of existing software :slight_smile:

Another idea for how to potentially create a huge map:

Procedurally generate the map, play around with the seed until you have a nice base map. Then take that and manually make changes to fine-tune the map. Save these changes as delta/diffs against the base map. If you didn’t make too many changes, the data, i.e. seed + changes, should only take small amount of memory.

(Pharap) #22

Sprites live in progmem so that would only save progmem.

It has software floating points, but they’re not always optimal since floats are 32 bits and the processor operates on 8-bit data.

That’s one of the reasons I wrote a fixed point library.

No C++ stdlib, only C stdlib.

Partly because most of the C++ containers use dynamic memory allocation, which is bad for obvious reasons.

That said, if there’s something specific that you want from the C++ stdlib that isn’t available,
I’m willing to have a go at building it.

And if it involves templates, I’m probably the best person to ask anyway.
I understand template voodoo.

(Miloslav Číž) #23

That’s one of the reasons I wrote a fixed point library.

Awesome :slight_smile:

only C stdlib.

Actually for this kind of low level programming I’ll prefer C (style) anyway.

I understand template voodoo.

Woah :smiley: Yeah I know templates are Turing complete but I probably don’t want to know any more :smiley: BTW I love Haskell, even though I only used it once (was thinking about Arduboy port here). Can’t say the same about templates.

(Pharap) #24

Why? C++ style is no less efficient than C style.

There are some expensive features (dynamic memory allocation, virtual functions) but those are usually easy enough to avoid.

I find turing completeness isn’t a particularly useful metric.
A 5 year old with a bucket of sticks is probably turing complete,
but that doesn’t mean a 5 year old with a bucket of sticks is useful for making software.

Templates can be practical even without fancy metaprogramming magic.
That project isn’t something I’d necessarily use for programming,
but it was a fun and useul programming exercise.

From experience I can safely say that templates do not deserve the contempt they receive.

A lot of people say they increase code size, but that’s usually false.
Most people who say that aren’t comparing them to the equivalent non-template code, they’re comparing to an alternate approach to solving the problem.
They can produce additional code if mishandled, but there’s almost always a good reason for that additional code and there’s also usually a way around it.

Most of the people who say bad things about them either don’t understand how they work or are just terrified of the syntax.
If used correctly, templates are safer than most C-style functions and can be safer and more efficient than function macros.

I was terrified of Haskell’s syntax until I put the effort in to learn it.
I still think a lot of it is cryptic and involves a bit of mental gymnastics, but I don’t doubt its usefulness.

(Miloslav Číž) #25

I just find the whole template idea not elegant, that’s all. It’s an additional language within an already pretty complex language, you know, and then there are the weird things like having to put implementations into header files etc. I hate when compilers cause this friction. I’m no expert here and I’m not saying I know a better solution, but there’s something weird about templates. I use them in situations where their use is obvious, but I’m never happy about having to use them. When comparing C to C++ I tend to prefer the minimalism of C, but I have yet to find a language I’d be at least 99% happy with regarding the syntax and all the other stuff. Haskell is the closest so far.

(Pharap) #26

I don’t want to derail the thread too much so this shall be my last post about templates.

When used correctly, I find them to be more elegant than other approaches.

An Example (click to reveal)

For example, consider:

SomeStruct a;
SomeStruct b;
std::memcpy(&a, &b, sizeof(SomeStruct));

And compare it with:

SomeStruct a;
SomeStruct b;
memoryCopy(a, b);

Made possible thanks to the template function:

template< typename T >
T & memoryCopy(T & destination, const T & source)
	return *std::memcpy(&destination, &source, sizeof(T));

The latter is less typing, completely type safe and doesn’t have to even think about null pointers.

With type_traits.h it’s even possible to make it cause a compile error if the programmer tries to invoke undefined behaviour.

To quote cppreference:

If either dest or src is a null pointer, the behavior is undefined, even if count is zero.

If the objects are not TriviallyCopyable, the behavior of memcpy is not specified and may be undefined.

The following definition would prevent both those situations:

template< typename T, bool isTriviallyCopyable = std::is_trivially_copyable<T>::value >
T & memoryCopy(T & destination, const T & source)
	static_assert(isTriviallyCopyable, "Type is not trivially copyable, memcpy is undefined. Use memoryCopy<T, true> to override");
	return *std::memcpy(&destination, &source, sizeof(T));

That may not be very elegant to write, but it only has to be written once.
To the end user, the API isn’t difficult - it’s called just like a normal function, there’s no sizeof or pointers to wrangle, and if the compiler is doing its job (which is most of the time) the template function will be inlined anyway, so the end result will be the same machine code.

There’s nothing to stop people doing that with non-template classes or functions.

The reason it has to be that way is because of the archaic compiler design that C++ inherited from C.
.cpp files are compiled as separate translation units - they never see each other’s contents.
That wouldn’t work for templates because templates aren’t classes or functions, they’re just a pattern used for generating classes and functions, hence the whole pattern needs to be known.

Macros can’t have their definitions separated into a .cpp file for a similar reason - macros are just patterns describing ways to manipulate text.

It’s still possible to put the implementation into a separate file from the definition, but it can’t be a .cpp file, it would have to be another header file (or as some have taken to doing, a .tpp file), and that file would have to be included at the end of the first.
I’ve used a similar technique to this before when writing particularly large templates.

Isocpp has a very good explanation here

I find using C’s restrictions causes more work.
E.g. being forced to do func(a, b) instead of a.func(b), and being worried about using the wrong kind of a. Having to worry about null pointers, those sorts of things.

Unfortunately Haskell isn’t particularly suitable for embedded systems. A lot of its constructs are memory hungry and it relies on a garbage collector.

To quote something I found on ycombinator:

The problem with Haskell is that while it is very effective at expressing ideas and logic, it is just as ineffective at expressing runtime behavior. In world where everything is lazily evaluated by default and garbage collected, it is very difficult to reason about how the code will actually execute. Are you trashing the cache? Fragmenting your heap? Are you misusing the instruction cache?

Which is quite close to my sentiment.
I know C++ intimately and I can take a very good guess at what the actual behaviour of the program is in terms of the stack, the heap and sometimes even the assembly generated, and I can usually do that even if I’m using several layers of templates and abstraction.
I cannot say the same for Haskell.

If you miss type inference though, C++ has the auto keyword:

const auto a  = 5;
const auto b = 10;
const auto c = a + b;

(Miloslav Číž) #27

Good points… let’s follow your suggestion and leave further discussion of templates to other times and places then :slight_smile:

I’ve just finished Arduventure – it’s a very nice game, kinda short but that’s because of the memory constraints. I think the project of creating the free assets would be worth it. Am currently reading through the code and trying to come up with my original asset replacements. I’d probably call the game ArduRPG – Google returns no results so I guess the name is not taken. So let me hereby reserve it :slight_smile: Here’s my concept for the logo:


TBH looks pretty awful but I like to have things documented. Let me see if I can come up with something better.


This probably looks better:




(Miloslav Číž) #28

OMG. Texture-mapped raycasting… so neat… I’d so want to make a game out of this as the next project :slight_smile:

If we could also texture the ceiling and/or the floor that would be some Doom stuff right here :slight_smile:

Also I’ve been thinking about level compression for this kind of mazes and have an idea involving RLE and Huffman coding that could work. But it probably already exists, like everything. Will do some research.

(Simon) #29

I hadn’t seen this one but that is well done. As you say, almost a Doom engine!

(Holmes) #30

Texture on the top and bottom may actually make it really hard to tell what’s going on in the screen unless you have some outline around things.

(Miloslav Číž) #31

True, floor would most likely be disturbing, but ceiling wouldn’t interfere that much. Pure black just looks very empty, however I dunno how to draw a textured ceiling without having to cast a ray for each pixel, which would be expensive. An acceptable halfway solution could be something like a fog.

Also a big problem to solve: controls. You need both turning and strafing => not enough buttons :frowning: Maybe something like:

forward/backward – move forward/backward
left/right – turn left/right
A – shoot
B + left/right – strafe left/right
B + up – cycle weapons
B + down – menu

With an option to switch turning and strafing. I wonder what these controls would feel like.

(Simon) #32

You could simply have a dithered ceiling or floor - a simple alternating pattern. I agree with @crait though, if you could get a black outline around the walls and other objects they elements will all run together.

Ard-Drvin’ used a modified display() command that populated the screen buffer with a pattern (instead of blank) when flushing of the screen buffer to the display. Doing this at the same time as the flush saves processing time and utilises dead time while waiting for the SPI transfers to complete.

At the end of each loop() you call display() which flushes the screen buffer to the actual display and resets the entire screen to a dithered background, ready for you to ray cast over the top of next loop iteration.

(Miloslav Číž) #33

Quick experiment :slight_smile:

video (sorry for crappy quality, ran slow in emulator)

diff ARCE_new.cpp ARCE.cpp 
<       uint8_t offsets[10] = {0,1,1,2,2,2,3,3,4,4};
<       uint8_t bgY = 0;
<       for (uint8_t i = 0; i < 10; ++i)
<       {
<         bgY += offsets[i];
<         if (bgY >= projectedSliceY)
<           break;
<         display.drawPixel(projectedSliceX, bgY, 1);
<         display.drawPixel(projectedSliceX, 63 - bgY, 1);
<       }
<       uint8_t len = min(4,projectedSliceY);
<       display.drawFastVLine(projectedSliceX + 1, 0, len, 1);
<       display.drawFastVLine(projectedSliceX + 1, 63, -len, 1);
<       uint8_t fog = 6 - min(4,projectedSliceHeight / 8);
<         if (fog < 2)
<         {
<           display.drawPixel(projectedSliceX, projectedTexelY, texel);
<           display.drawPixel(projectedSliceX + 1, projectedTexelY, texel);          
<         }
<         else if (fog < 3)
<         {
<           display.drawPixel(projectedSliceX, projectedTexelY, texel);
<           if (projectedSliceRenderY % 2 == 1)
<             display.drawPixel(projectedSliceX + 1, projectedTexelY, texel);        
<         }
<         else if (fog < 4)
<         {
<           display.drawPixel(projectedSliceX + projectedTexelY % 2, projectedTexelY, texel);
<         }        
<         else if (fog < 5)
<         {
<           if (projectedSliceRenderY % 2 == 0)
<             display.drawPixel(projectedSliceX, projectedTexelY, texel);
<         }
>         display.drawPixel(projectedSliceX, projectedTexelY, texel);
>         display.drawPixel(projectedSliceX + 1, projectedTexelY, texel);

EDIT: Note that the background dithering pattern is wrong, I just quickly smashed together something that would look close. Also the fog dithering pattern probably transitions badly between different levels, couldn’t be bothered to look at these details, plus the fog distance could be played around and fine-tuned. All in all fixing these things the results could look much better (in case anyone’s bored).

(Miloslav Číž) #34

Another game idea: tower defense. I simply love that genre. Here’s a quick concept screen:


The game could work like this:

  • At the start screen choose one of N (like 3) maps.
  • Gameplay is typical: build towers, earn money, upgrade towers.
  • There would be an infinite number of rounds, you’d be playing for the highest score and/or highest round you can survive.
  • The map format would be this: You simply specify roads which the creeps take from start to finish. E.g. the map in the picture would be defined as:
    path1: `start: 1,1; 3 steps down; 2 steps right; 2 steps down; ...`
    path2: `start: 1,7; 3 steps up; 2 steps right; 2 steps down; ...`
    Based on that the road would be created before the game starts. Such map would also take very little space so there could eventually be a lot of them. Simple and elegant, right? :slight_smile:
  • Also each map would have a function pointer associated with it to spawn creeps based on the round number.
  • People would be encouraged to create and share custom maps. Maybe a simple map editor could be written in HTML5 or something.


tower concepts:


enemy concepts:



ideas for monster special abilities:

  • health regeneration (lizard?)
  • immunity to certain attacks (ghost?)
  • on death spawns multiple of other monsters (spider?)

(Pharap) #35

Probably something like this:

#pragma once

#include <stdint.h>

enum class Direction : uint8_t
	North = 0x00,
	East = 0x01,
	South = 0x02,
	West = 0x03,

class PathStep
	uint8_t data = 0;
	static constexpr CountShift = 2;
	static constexpr DirectionShift = 0;
	static constexpr CountMask = 0x3F;
	static constexpr DirectionMask = 0x03;
	constexpr PathStep(void) = default;

	constexpr PathStep(Direction direction, uint8_t count)
		: data(((count & 0x3F) << CountShift) | ((static_cast<uint8_t>(direction)& DirectionMask) >> DirectionShift))
	constexpr uint8_t getData(void) const
		return this->data;
	constexpr Direction getDirection(void) const
		return static_cast<Direction>((this->data >> DirectionShift) & DirectionMask)
	constexpr uint8_t getCount(void) const
		return (this->data >> CountShift) & CountMask;

struct PathPoint
	uint8_t x = 0;
	uint8_t y = 0;
	constexpr PathPoint(void) = default;	
	constexpr PathPoint(uint8_t x, uint8_t y) : x(x), y(y) {}

struct PathData
	PathPoint position;
	uint8_t pathLength;
	const PathStep * path;
	template< size_t size >
	constexpr PathData(PathPoint position, PathStep (&path)[size])
		: position(position), pathLength(size), path(path) {}

// Data

const PathStep Path1[] PROGMEM =
	PathStep(Direction::South, 3),
	PathStep(Direction::East, 2),
	PathStep(Direction::South, 2),
const PathData Path1Data PROGMEM = PathData(PathPoint(1, 1), Path1);

const PathStep Path2[] PROGMEM =
	PathStep(Direction::North, 3),
	PathStep(Direction::East, 2),
	PathStep(Direction::South, 2),
const PathData Path2Data PROGMEM = PathData(PathPoint(1, 7), Path2);

(Miloslav Číž) #36

That’s the “classy” way to do it :slight_smile: Very nice OOP exercise.

(Miloslav Číž) #37

Have some basic map drawing in place :slight_smile:






(Miloslav Číž) #38

Getting there :smiley:


(Holmes) #39

This is looking cool!

(Liam) #40

Could I have a link to that Tamagotchi clone if you can find it? I did have a look… they where my Fav to play when I was a kid :slight_smile: