Advertisement

Spaceolopy

Started by July 25, 2022 01:09 AM
86 comments, last by pbivens67 1 year, 11 months ago

I have shot some bugs : )

Unfortunately i can't make a screenshot fast enough while bullet is still on screen.
It's pretty boring because the bugs don't move, but it works to shoot them.

pbivens67 said:
then what is a data structure?

The structs i use in the code below.
Notice how easy it becomes to deal with many bugs, as one more bug is just one more element in the std::vector of bugs. I could use an array of bugs too, or a list, etc.
It's straight forward. Most work with writing this actually was the SetupLevel() function, which sets up all the data.
After that, the data lives in memory, not in magic numbers scattered across copy pasted functions.
I write my functions just once, which i can do because i use data structures.

So you should not write multiple similar functions, just because the data differs. Data and functionality should be separate things.
You want to write one generic function, and all the permutations, e.g. two bugs having a different position, should be expressed by having different data.
So you can just loop over the data, processing it all with the same function. It's less code, and it can scale up to increasing complexity.
There should be no difference of having 2 bugs or 100 bugs, for you as the programmer. You never want to copy paste a line of code 100 times just because you now have 100 bugs.

I hope it helps…

{
	// this code runs every frame, basically in my rendering loop
	// i will use static variables to preserve state between frames, you would use global variables instead
	// and i have to use lambdas insead normal functions, so i can write functions inside functions. you would just use normal functions instead
	// to try the game quickly, you would only need to paste all the code as is in your rendering loop too, and replacing drawing and input


	// the very first thing we do, we make us some DATA STRUCTURES:

	struct Box 
	{
		float x, y, w, h;
	};

	struct Bug : Box
	{
		Box posSize; // our box gives us position and size of a bug
		bool alive = true;
	};

	struct Bullet
	{
		Box posSize; // yay, we can use the same data structure here again! Isn't that nice?
		float velX, velY;
	};

	struct Player
	{
		Box posSize;
	};

	/* note: We could spare a lot typing of 'posSize', if we would use Box as a base class, e.g. 
	struct Bug : Box
	{
		bool alive = true;
	};
	Then we could write just bug.x = 0 instead bug.posSize.x = 0. But that's a matter of taste.
	*/

	// that's all we need, now let's bundle it together to make a game out of it

	struct Game
	{
		std::vector<Bug> bugs; // we'll use many bugs!
		Bullet bullet; // but only one bullet for now
		Player player;
		Box screen = {0,0,0.640f,0.480f}; // i make my sscreen very small, so i can find it easily with my usual projection

		void SetupLevel (int resX, int resY)
		{
			Box bugArea = screen;
			bugArea.h *= 0.75f;
			bugArea.y += screen.h * 0.25f;

			Box bugSize;
			bugSize.w = .5f * bugArea.w / float(resX);
			bugSize.h = .5f * bugArea.h / float(resY);

			for (int y=0; y<resY; y++)
			for (int x=0; x<resX; x++)
			{
				float tx = (float(x) + .25f) / float(resX);
				float ty = (float(y) + .25f) / float(resY);
				Bug bug;
				bug.posSize = bugSize;
				bug.posSize.x = bugArea.x + tx * bugArea.w;
				bug.posSize.y = bugArea.y + ty * bugArea.h;
				bugs.push_back(bug);
			}

			player.posSize = bugSize;
			player.posSize.x = (screen.w - bugSize.w) * 0.5f; 
			player.posSize.y = 0; 

			bullet.posSize.w = bugSize.w * 0.1f;
			bullet.posSize.h = bugSize.w * 0.5f;
			bullet.posSize.x = 0;
			bullet.posSize.y = screen.h;
		};

		void Init ()
		{
			SetupLevel (8, 5);
		}

		bool IsBulletLaunced ()
		{
			return (bullet.posSize.y < screen.h);
		}

		void PlayerInput (Application &app, float timestep)
		{
			ImGui::Text("timestep %f", timestep);

			float speed = screen.w * 0.5f;

			if (app.KeyDown(Application::KEY_LBUTTON))
			{
				player.posSize.x = max(player.posSize.x - speed * timestep, screen.x);
			}
			if (app.KeyDown(Application::KEY_RBUTTON))
			{
				player.posSize.x = min(player.posSize.x + speed * timestep, screen.x + screen.w - player.posSize.w);
			}
			if (app.KeyDown(Application::KEY_MBUTTON))
			{
				if (!IsBulletLaunced()) // can only shoot if bullet left the screen
				{
					bullet.posSize.x = player.posSize.x + (player.posSize.w - bullet.posSize.w) * 0.5f;
					bullet.posSize.y = player.posSize.y + player.posSize.h;
				}
			}
		}

		void UpdatePhysics (float timestep)
		{
			float speed = screen.w * 0.75f;

			bullet.posSize.y += speed * timestep;

			// todo: animate bugs



			// collsion detection

			// we have some generic function for the collision test

			auto AABoxIntersection = [] (	float b1minX, float b1minY, float b1maxX, float b1maxY, 
											float b2minX, float b2minY, float b2maxX, float b2maxY )
			{
				if (b1maxX < b2minX || b1minX > b2maxX) return false;
				if (b1maxY < b2minY || b1minY > b2maxY) return false;
				return true;
			};

			// plus some tooling function for our Box convention

			auto Intersection = [&] (Box a, Box b)
			{
				return AABoxIntersection (	a.x, a.y, a.x+a.w, a.y+a.h, 
											b.x, b.y, b.x+b.w, b.y+b.h );
			};

			// the loop to collide bullet with all bugs

			if (IsBulletLaunced()) for (Bug &bug : bugs)
			{
				if (!bug.alive)
					continue;

				if (Intersection(bullet.posSize, bug.posSize))
				{
					bug.alive = false; // kill bug
					bullet.posSize.y = screen.h; // kill bullet
					break;
				}
			}

		}

		void Render ()
		{
			auto RenderBox = [](Box box, float r, float g, float b)
			{
				vec2 p0(box.x, box.y);
				vec2 p1 = p0 + vec2(box.w, box.h);
				RenderAABBox (p0, p1, r,g,b);
			};

			RenderBox (screen, 1,1,1); // white screen border

			for (Bug &bug : bugs) // render bugs as blue squares
			{
				if (bug.alive) RenderBox (bug.posSize, 0,0.5f,1);
			}

			if (IsBulletLaunced()) 
				RenderBox (bullet.posSize, 1,0.5f,0); // orange bullet

			RenderBox (player.posSize, 0,1,0); // green player
		}
	};


	// create the game and initialize it once

	static Game game;

	static bool firstRun = true;
	if (firstRun) // do this only once
	{
		firstRun = false;

		game.Init();
	}

	// sync to realtime
	
	static auto prevTime = std::chrono::system_clock::now();
	auto curTime = std::chrono::system_clock::now();
	std::chrono::duration<float> deltaTime = curTime - prevTime;
	prevTime = curTime;

	// game loop

	game.PlayerInput (application, deltaTime.count());
	game.UpdatePhysics (deltaTime.count());
	game.Render();

}

WOW!! that is a lot of code honestly, I cannot code at that level, I am going to get my c++ book out again and do some exercises, because it has been a long time since I have been in college

Advertisement

pbivens67 said:
I am going to get my c++ book out again

It should be mostly C, but i can't because i wanted to write it all in one self contained scope.

The things which may feel pretty new are:

auto Intersection = [&] (Box a, Box b)

That's a lambda, but can be rewritten as a normal function:

bool Intersection (Box a, Box b)

So you don't kneed to know about lambdas to read or port this.

for (Bug &bug : bugs)

is the same as:

for (int i=0; i<bugs.size(); i++) { Bug &bug = bugs[i]; … (or a better syntax using iterators)

So it just iterates all elements of the vector in order

std::chrono stuff is confusing i think, but it's just measuring the duration since the last iteration and finally gives me seconds as a float value.

So i assume the things you might not know are not important either. Otherwise just ask.

is there a better way to learn coding?

pbivens67 said:
is there a better way to learn coding?

Personally i've learned C from this book: http://cslabcms.nju.edu.cn/problem_solving/images/c/cc/The_C_Programming_Language_%282nd_Edition_Ritchie_Kernighan%29.pdf

Pretty much the only book about programming i did ever read. But it was all i needed. Actually i did not have the book, but it was text files plus code files as well. I think i got this from some shareware CD Rom.

I think nowadays we have online courses, with online IDE and compiler so you can run and execute, and you can modify code to see what happens.
Maybe this is an example:

https://www.tutorialspoint.com/cprogramming/c_quick_guide.htm

It has ‘Live Share’ button to bring up online IDE and code. Seems pretty good.

But i'm surprised you need to learn to understand the given code.
I assume it's the structs, which cause confusion?

Structs in C are basically a custom data type, e.g.:

struct Thing
{
	float a;
	int b;
};

int main ()
{
	int k = 5;
	
	Thing l; l.a = 0.1f; l.b = 5;
	
	// we can also make an array of Things, jast as with basic data types:
	
	Thing things[10];
	things[3].b = 7;
}

For the array example, the data in memory is in this order: a,b,a,b,a,b,a,b…

If we would make make arrays of basic types instead:

float aThings[10];
int bThings[10];

We would get this order in memory: a,a,a,a,a… b,b,b,b,b…

So the struct is a way to group some data we usually process commonly. We can use it also as function parameters:

void ProcessThing (Thing myThing)
{
	float k = myThing.a;
}

So we no longer need to give many parameters, just one Thing.
So it helps to keep things organized.

Later, with C++, structs also got member methods (beside other new features):

struct Thing
{
	float a;
	int b;
	
	int CalcSum()
	{
		return a + b; // the method already has access to the data of it's instance of a struct (or class)
	}
};

Now we can do:

Thing l; l.a = 10.1f; l.b = 5;

int sum = l.CalcSum(); // will be 15

So now we can group no only data, but also related functionality together.

Which is the basic idea of ‘object oriented’ concept. We now have objects with their own data and functionality.

Advertisement

well I am reading C++ How to program by dietel and dietel, it seems like a good book with lots of exercises, college level

This topic is closed to new replies.

Advertisement