Fizzy Ride - Tweet Jam

I saw this tweet and had to make a game about it, this is just a demo, but check it out!

Fizzy Ride.hex

Controls: Up and Down, that’s it!

I made this entirely in the emulator in about 2 hours! (Not bad considering I had to remember how if statements are formatted.)

To-Do:
Inverted bitmap puffs
Puff “flare” expands in the Y direction. Currently my library can give an initial velocity, but I need one that accumulates over time.
Background Clouds. I would like to make them interactive, collision based and “poof” and split off in entertaining ways. I could use help on this, a circular collision detection implementation.

Then: gameplay??

Special thanks to the artwork creator!

FizzyRide

6 Likes

Circle-circle or circle-rectangle?

Circle-circle is really easy, all you do is measure the distance between the centres of the circles and see if that distance is less than the sum of the circles’ radii.

struct Circle
{
	int16_t x;
	int16_t y;
	uint8_t radius;

	constexpr int16_t getDiameter() const
	{
		return (radius * 2);
	}
};

// More accurate, slightly expensive, float-using version
bool areColliding(const Circle & circleA, const Circle & circleB)
{
	auto xDifference = (circleA.x - circleB.x);
	auto yDifference = (circleA.y - circleB.y);
	auto squareDistance = ((xDifference * xDifference) + (yDifference * yDifference));
	auto distance = sqrt(squareDifference);
	auto combinedRadius = (circleA.radius + circleB.radius);

	return (distance <= combinedRadius);
}

// Less accurate, cheaper, integer-only  version
bool areColliding(const Circle & circleA, const Circle & circleB)
{
	auto xDifference = (circleA.x - circleB.x);
	auto yDifference = (circleA.y - circleB.y);
	auto squareDistance = ((xDifference * xDifference) + (yDifference * yDifference));
	auto combinedRadius = (circleA.radius + circleB.radius);
	auto squareRadius = (combinedRadius * combinedRadius);

	return (squareDistance <= squareRadius);
}

I’m fine assuming the airplane is a circle.

What I want to do is figure out if the plane is within a certain interior bounds of the background clouds and if so to move the cloud out of the way, possibly split it in half if it’s right in the middle of it. I guess that’s going to involve finding a vector between the two bodies and then applying that as an impulse force to the cloud.

*hopes pharap says it’s easy and pastes the result heh heh

Circle-rectangle collision is also doable, but the maths makes my eyes glaze over.

This one looks easier to understand:

I have no clue what you mean by this.

Partly because I don’t know what these ‘background clouds’ are supposed to look like/how they’re supposed to be structured, but mostly because I have no clue what ‘certain interior bounds’ is supposed to mean.

Splitting the clouds means the clouds will probably have to be entities, so you’d effectively end up removing one entity and adding two different enttities.

That part is relatively easy.

Does this help?

EDIT: I guess if I’m only pushing clouds mostly up and down I don’t really NEED a true vector I can just use one dimension.

Hi! Artist here, thanks a lot for making this a demo, you are awesome.
I have a couple ideas on gameplay, I’ll be wrapping them around and making a GDD of some sorts.
If you are interested let me know where we can talk about it :smile:

1 Like

Sure! Post it up here or you can PM me!

1 Like

If you limited the division capability in such a way that the plane can only divide a cloud if it flies into it from the left hand side then this should be relatively easy.

Thay way you could detect the division point by checking how far away the plane’s y is from the cloud’s y (with the y being the centre, not the top), which would tell you if it’s close enough vertically to cause a division, otherwise if it isn’t close enough then a collision results in a ‘push’ rather than a ‘split’. (With other approaches, the question of when to push and when to split gets more complicated.)

Pushing is a bit more complicated - it depends how you expect the cloud to behave (which direction it should travel in) after the plane hits it. E.g. should it be repelled and ‘slide off’ the plane, or should it be pushed in the direction that the plane was travelling?

Making it move in the direction the plane was travelling is unrealistic, but easy, you’d just copy the plane’s velocity (maybe reduce it a bit) and the cloud would move away (and keep moving if there’s no friction or no rule for stopping it).

The more realistic the collisions have to be the more complicated it gets.

Fizzy Ride.hex

Added Clouds and inverted poofs!

Um… drawing circles looks to be very inefficient. I think I’m probably better off just picking a few large cloud sizes and using draw bitmap for them also… ?

3 Likes

If you do self masked images, you only need a handful of circles to make any clouds.

@bateske @DamianArtClub - love it!
A little fluffy cloud world to fly around in and just relax. Beautiful!
:cloud: :flight_departure: :cloud::cloud: :cloud:

1 Like

Trying to think of a gameplay element that would encourage/cause you to want to be on the right side of the screen.

I was thinking maybe you like, press a to boost through a cloud to burst it?

Or maybe, tossing something back behind you, or running away from something?

Hi Kevin! Heres a link to the GDD I currently have, I’m still missing some visual examples but the main ideas are here.

I wanted to ask you for your Email to give you editor/commenter access to the doc, but I was unable to PM trough twitter.
Lmk if you have some ideas to discuss!

Perhaps, erm, ‘Cloud Busting’ for a soundtrack…?! :wink:

Having some problems where with… I think fixed point math??

ArduboyRecording

I’m using a very simple particle library @pharap helped me with and the issue I’m having if I set a positive Y velocity, the coefficient of friction is correctly applied. However if it’s a negative number the particle (cloud) continues it’s movement… not being slowed down by the cF.

  if( areColliding(pSize,x,y) ){ // collision
    arduboy.drawCircle( x, y, pSize, 1);
    int yDiff = ( (plane_y ) - y );
    if(yDiff > 0){
      dotsLanded[i].setVelY( -1.1 ); // cloud flys up forever
    }
    else{
      dotsLanded[i].setVelY( 1.1 );  // works
    }
  }
  else{ // no collision 
    arduboy.fillCircle( x, y, pSize, 1);    
  }

and in particle.h

private:
        SQ7x8 velx = 0;
        SQ7x8 vely = 0;
public:
        static constexpr SQ7x8 cF = 0.96;
        void setVelX(SQ7x8 val)                  { this->velx = val; }
        void setVelY(SQ7x8 val)                  { this->vely = val; }
public:
        void update() {

        if(this->type == 1){  // clouds
          this->x -= 1;
      
          this->vely *= cF;
          this->y += static_cast<int8_t>(this->vely);
        }

}

I guess I’m doing something wrong with the signing, but it just makes no sense to me how to fix it.

Looking like multiplying by a negative number using this fixed point library causes issue?

Here is a zip of the code:
Fizz1.zip (310.7 KB)

Update it looks like the library will resolve multiplications of a negative number truncated to like -1?? Like I can’t go from -1 to 0 by multiplying by the cF (0.9) But it goes from 1 to 0 with the same multiplication.

Depending on how rounding is performed, this could be from the truncation after the fixed point multiplication by cF: since truncation rounds toward negative infinity, it could result in vely returning to its original value after the multiplication. Maybe you could try something like:

vely += SQ7x8(0.5 / 0.96 / 256);
vely *= cF;
1 Like

I think you’re right! Thank you! I’m kind of confused where to put those operations though.

I added this catch to pull it down to 0, but would like something more… correct, or elegant:

        if(this->type == 1){

          this->x -= 1;
          this->vely *= cF;
          if(this->vely < 0 && this->vely > -1){
            this->vely=0;
          }
          this->y += static_cast<int8_t>(this->vely);
        }

The this->vely += SQ7x8(0.5 / 0.96 / 256); line goes before this->vely *= cF; in the update method. The hope is to preadd a value that will result in a +0.5 offset after multiplication, which will make truncation act like round to nearest.

Now all the clouds just float up on their own lol

OK, hacky version here, avoids the preadd in favor of adding a precise 0.5 offset after the product:

this->vely = (((int32_t)vely.getInternal() * (int32_t)cF.getInternal()) + 0x80) >> 8;