티스토리 뷰

* 인프런에 있는 "홍정모의 게임 만들기 연습 문제 패키지" 강의를 바탕으로 작성된 글입니다.

 

1. 마우스 클릭한 곳에 폭발 효과

#include "Game2D.h"
#include "RandomNumberGenerator.h"
#include "RigidCircle.h"
#include <vector>
#include <memory>

namespace shyplants
{
	using namespace std;
	static const auto gravity = vec2(0.0f, -9.8f);

	class Particle
	{
	public:
		vec2 pos;
		vec2 vel;
		RGB  color;

		float rot;
		float angular_vel;

		float age;
		float life;

		void update(const float & dt)
		{
			const auto acc = gravity; //assumes GA only.

			vel += acc * dt;
			pos += vel * dt;

			rot += angular_vel * dt;

			// update age.
			age += dt;
		}
	};

	class ParticleSystem
	{
	public:
		vector<Particle> particles;
		RandomNumberGenerator rn;
		vec2 init_pos = { 0.0, 0.5f };

		ParticleSystem()
		{
			// reset(init_pos);
		}

		auto getRandomUnitVector()
		{
			const float theta = rn.getFloat(0.0f, 3.141592f * 2.0f); // 0.0 ~ 2pi

			return vec2{ cos(theta), -sin(theta) };
		}

		auto getRandomColor()
		{
			return RGB{ rn.getFloat(0.0f, 1.0f), rn.getFloat(0.0f, 1.0f), rn.getFloat(0.0f, 1.0f) };
		}

		void reset(vec2 init_pos)
		{
			particles.clear();

			// initialize particles
			for (int i = 0; i < 1000; ++i)
			{
				Particle new_particle;
				new_particle.pos = init_pos + getRandomUnitVector() * rn.getFloat(0.001f, 0.03f);
				new_particle.vel = getRandomUnitVector() * rn.getFloat(0.01f, 2.0f);
				new_particle.color = getRandomColor();
				new_particle.age = 0.0f;
				new_particle.life = rn.getFloat(0.1f, 0.5f);
				new_particle.rot = rn.getFloat(0.0f, 360.0f);
				new_particle.angular_vel = rn.getFloat(-1.0f, 1.0f) * 360.0f * 4.0f;

				particles.push_back(new_particle);
			}
		}

		void update(const float & dt)
		{
			for (auto & pt : particles)
			{
				pt.update(dt);
			}
		}

		void draw()
		{
			beginTransformation();

			for (const auto & pt : particles)
			{
				if (pt.age > pt.life) continue;

				const float alpha = 1.0f - pt.age / pt.life;
				const RGB blended_color = { pt.color.r * alpha + 1.0f * (1.0f - alpha),
											pt.color.g * alpha + 1.0f * (1.0f - alpha),
											pt.color.b * alpha + 1.0f * (1.0f - alpha) };

				// drawPoint(blended_color, pt.pos, 3.0f);

				beginTransformation();
				translate(pt.pos);
				rotate(pt.rot);
				drawFilledStar(blended_color, 0.03f, 0.01f);
				endTransformation();
				
			}

			endTransformation();
		}
	};

	class Example : public Game2D
	{
	public:
		ParticleSystem ps;
		vec2 mouse_pos;

		Example()
			: Game2D()
		{
			reset();
		}

		void reset()
		{
			// ps.reset();
		}

		void update() override
		{
			const float dt = getTimeStep() * 0.4f;

			ps.update(dt);
			ps.draw();

			// reset button
			if (isKeyPressedAndReleased(GLFW_KEY_R)) reset();

			mouse_pos = getCursorPos(true);
			if (isMouseButtonPressedAndReleased(GLFW_MOUSE_BUTTON_1)) {
				ps.reset(mouse_pos);
			}
		}
	};
}

2. 스프링클러에서 물이 뿜어져 나오듯이 지속적으로 파티클 생성

#include "Game2D.h"
#include "RandomNumberGenerator.h"
#include "RigidCircle.h"
#include <vector>
#include <memory>

namespace shyplants
{
	using namespace std;
	static const auto gravity = vec2(0.0f, -9.8f);

	class Particle
	{
	public:
		vec2 pos, init_pos;
		vec2 vel, init_vel;
		RGB  color;

		float rot, init_rot;
		float angular_vel, init_angular_vel;

		float age;
		float life;

		void update(const float & dt)
		{
			const auto acc = gravity; //assumes GA only.

			vel += acc * dt;
			pos += vel * dt;

			rot += angular_vel * dt;

			// update age.
			age += dt;
		}
		
		void reset()
		{
			pos = init_pos;
			vel = init_vel;
			rot = init_rot;
			angular_vel = init_angular_vel;
			age = 0.0f;
		}
	};

	class ParticleSystem
	{
	public:
		vector<Particle> particles;
		RandomNumberGenerator rn;

		ParticleSystem()
		{
			// reset(init_pos);
		}

		auto getRandomUnitVector()
		{
			const float theta = rn.getFloat(0.0f, 3.141592f * 2.0f); // 0.0 ~ 2pi

			return vec2{ cos(theta), -sin(theta) };
		}

		auto getRandomColor()
		{
			return RGB{ rn.getFloat(0.0f, 1.0f), rn.getFloat(0.0f, 1.0f), rn.getFloat(0.0f, 1.0f) };
		}

		void reset(vec2 init_pos = { 0.0f, 0.5f })
		{
			particles.clear();

			// initialize particles
			for (int i = 0; i < 1000; ++i)
			{
				Particle new_particle;
				new_particle.init_pos = init_pos + getRandomUnitVector() * rn.getFloat(0.001f, 0.03f);
				new_particle.init_vel = getRandomUnitVector() * rn.getFloat(0.01f, 2.0f);
				new_particle.color = getRandomColor();
				new_particle.life = rn.getFloat(0.1f, 0.5f);
				new_particle.init_rot = rn.getFloat(0.0f, 360.0f);
				new_particle.init_angular_vel = rn.getFloat(-1.0f, 1.0f) * 360.0f * 4.0f;

				new_particle.reset();
				particles.push_back(new_particle);
			}
		}

		void update(const float & dt)
		{
			for (auto & pt : particles)
			{
				pt.update(dt);
			}
		}

		void draw()
		{
			beginTransformation();

			for (auto & pt : particles)
			{
				if (pt.age > pt.life) {
					pt.reset();
					continue;
				}

				const float alpha = 1.0f - pt.age / pt.life;
				const RGB blended_color = { pt.color.r * alpha + 1.0f * (1.0f - alpha),
											pt.color.g * alpha + 1.0f * (1.0f - alpha),
											pt.color.b * alpha + 1.0f * (1.0f - alpha) };

				// drawPoint(blended_color, pt.pos, 3.0f);

				beginTransformation();
				translate(pt.pos);
				rotate(pt.rot);
				drawFilledStar(blended_color, 0.03f, 0.01f);
				endTransformation();
			}

			endTransformation();
		}
	};

	class Example : public Game2D
	{
	public:
		ParticleSystem ps;

		Example()
			: Game2D()
		{
			reset();
		}

		void reset()
		{
			ps.reset();
		}

		void update() override
		{
			const float dt = getTimeStep() * 0.4f;

			ps.update(dt);
			ps.draw();

			// reset button
			if (isKeyPressedAndReleased(GLFW_KEY_R)) reset();
		}
	};
}

3. 시간에 따라 별이 점점 더 커지게 만들어 보기.

#include "Game2D.h"
#include "RandomNumberGenerator.h"
#include "RigidCircle.h"
#include <vector>
#include <memory>

namespace shyplants
{
	using namespace std;
	static const auto gravity = vec2(0.0f, -9.8f);

	class Particle
	{
	public:
		vec2 pos, init_pos;
		vec2 vel, init_vel;
		RGB  color;

		float rot, init_rot;
		float angular_vel, init_angular_vel;
		float scale;

		float age;
		float life;

		void update(const float & dt)
		{
			const auto acc = gravity; //assumes GA only.

			vel += acc * dt;
			pos += vel * dt;

			rot += angular_vel * dt;

			scale += dt * 5.0f;

			// update age.
			age += dt;
		}
		
		void reset()
		{
			pos = init_pos;
			vel = init_vel;
			rot = init_rot;
			angular_vel = init_angular_vel;
			age = 0.0f;
			scale = 1.0f;
		}
	};

	class ParticleSystem
	{
	public:
		vector<Particle> particles;
		RandomNumberGenerator rn;

		ParticleSystem()
		{
			// reset(init_pos);
		}

		auto getRandomUnitVector()
		{
			const float theta = rn.getFloat(0.0f, 3.141592f * 2.0f); // 0.0 ~ 2pi

			return vec2{ cos(theta), -sin(theta) };
		}

		auto getRandomColor()
		{
			return RGB{ rn.getFloat(0.0f, 1.0f), rn.getFloat(0.0f, 1.0f), rn.getFloat(0.0f, 1.0f) };
		}

		void reset(vec2 init_pos = { 0.0f, 0.5f })
		{
			particles.clear();

			// initialize particles
			for (int i = 0; i < 1000; ++i)
			{
				Particle new_particle;
				new_particle.init_pos = init_pos + getRandomUnitVector() * rn.getFloat(0.001f, 0.03f);
				new_particle.init_vel = getRandomUnitVector() * rn.getFloat(0.01f, 2.0f);
				new_particle.color = getRandomColor();
				new_particle.life = rn.getFloat(0.1f, 0.5f);
				new_particle.init_rot = rn.getFloat(0.0f, 360.0f);
				new_particle.init_angular_vel = rn.getFloat(-1.0f, 1.0f) * 360.0f * 4.0f;

				new_particle.reset();
				particles.push_back(new_particle);
			}
		}

		void update(const float & dt)
		{
			for (auto & pt : particles)
			{
				pt.update(dt);
			}
		}

		void draw()
		{
			beginTransformation();

			for (auto & pt : particles)
			{
				if (pt.age > pt.life) {
					pt.reset();
					continue;
				}

				const float alpha = 1.0f - pt.age / pt.life;
				const RGB blended_color = { pt.color.r * alpha + 1.0f * (1.0f - alpha),
											pt.color.g * alpha + 1.0f * (1.0f - alpha),
											pt.color.b * alpha + 1.0f * (1.0f - alpha) };

				// drawPoint(blended_color, pt.pos, 3.0f);

				beginTransformation();
				translate(pt.pos);
				rotate(pt.rot);
				scale(pt.scale, pt.scale);
				drawFilledStar(blended_color, 0.03f, 0.01f);
				endTransformation();
			}

			endTransformation();
		}
	};

	class Example : public Game2D
	{
	public:
		ParticleSystem ps;

		Example()
			: Game2D()
		{
			reset();
		}

		void reset()
		{
			ps.reset();
		}

		void update() override
		{
			const float dt = getTimeStep() * 0.4f;

			ps.update(dt);
			ps.draw();

			// reset button
			if (isKeyPressedAndReleased(GLFW_KEY_R)) reset();
		}
	};
}

4. 공 위에 파티클을 뿌릴 경우 물체와의 충돌

#include "Game2D.h"
#include "RandomNumberGenerator.h"
#include "RigidCircle.h"
#include <vector>
#include <memory>

namespace shyplants
{
	using namespace std;
	static const auto gravity = vec2(0.0f, -9.8f);

	class Particle
	{
	public:
		vec2 pos, init_pos;
		vec2 vel, init_vel;
		RGB  color;

		float rot, init_rot;
		float angular_vel, init_angular_vel;
		float scale;

		float age;
		float life;

		void update(const float & dt)
		{
			const auto acc = gravity;          //assumes GA only.
			const float coef_res = 0.8f;       // coefficient of restitution
			const float coef_friction = 0.99f; // friction (not physical)

			vel += acc * dt;
			pos += vel * dt;

			rot += angular_vel * dt;

			scale += dt * 5.0f;

			// update age.
			age += dt;
		}
		
		void reset()
		{
			pos = init_pos;
			vel = init_vel;
			rot = init_rot;
			angular_vel = init_angular_vel;
			age = 0.0f;
			scale = 1.0f;
		}
	};

	class ParticleSystem
	{
	public:
		vector<Particle> particles;
		RandomNumberGenerator rn;
		vec2 circle_pos = { 0.0f, -0.2f };
		float circle_radius = 0.3f;

		ParticleSystem()
		{
			// reset(init_pos);
		}

		auto getRandomUnitVector()
		{
			const float theta = rn.getFloat(0.0f, 3.141592f * 2.0f); // 0.0 ~ 2pi

			return vec2{ cos(theta), -sin(theta) };
		}

		auto getRandomColor()
		{
			return RGB{ rn.getFloat(0.0f, 1.0f), rn.getFloat(0.0f, 1.0f), rn.getFloat(0.0f, 1.0f) };
		}

		void reset(vec2 init_pos = { 0.0f, 0.5f })
		{
			particles.clear();

			// initialize particles
			for (int i = 0; i < 1000; ++i)
			{
				Particle new_particle;
				new_particle.init_pos = init_pos + getRandomUnitVector() * rn.getFloat(0.001f, 0.03f);
				new_particle.init_vel = getRandomUnitVector() * rn.getFloat(0.01f, 2.0f);
				new_particle.color = getRandomColor();
				new_particle.life = rn.getFloat(0.1f, 0.5f);
				new_particle.init_rot = rn.getFloat(0.0f, 360.0f);
				new_particle.init_angular_vel = rn.getFloat(-1.0f, 1.0f) * 360.0f * 4.0f;

				new_particle.reset();
				particles.push_back(new_particle);
			}
		}

		void update(const float & dt)
		{
			const float epsilon = 0.5f;

			for (auto& pt : particles)
			{
				pt.update(dt);
			}

			// check collision between particles and a circle
			for (auto& pt : particles)
			{
				const float distance = (pt.pos - circle_pos).getMagnitude();
				if (distance <= 0.03f * pt.scale + circle_radius)
				{
					// compute impulse
					const auto normal = (pt.pos - circle_pos) / distance;

					if (pt.vel.getDotProduct(normal) < 0.0f)  // approaching
					{
						
						const auto impulse = normal * -(1.0f + epsilon) * pt.vel.getDotProduct(normal) /
							((1.0f / 5.0f) + (1.0f / pt.scale));

						pt.vel += impulse / pt.scale;
					}
				}
			}
		}

		void draw()
		{
			beginTransformation();
			
			beginTransformation();
			translate(circle_pos);
			drawFilledCircle(Colors::red, 0.3f);
			endTransformation();

			for (auto & pt : particles)
			{
				if (pt.age > pt.life) {
					pt.reset();
					continue;
				}

				const float alpha = 1.0f - pt.age / pt.life;
				const RGB blended_color = { pt.color.r * alpha + 1.0f * (1.0f - alpha),
											pt.color.g * alpha + 1.0f * (1.0f - alpha),
											pt.color.b * alpha + 1.0f * (1.0f - alpha) };

				// drawPoint(blended_color, pt.pos, 3.0f);

				beginTransformation();
				translate(pt.pos);
				rotate(pt.rot);
				scale(pt.scale, pt.scale);
				drawFilledStar(pt.color, 0.03f, 0.01f);
				endTransformation();
			}

			endTransformation();
		}
	};

	class Example : public Game2D
	{
	public:
		ParticleSystem ps;

		Example()
			: Game2D()
		{
			reset();
		}

		void reset()
		{
			ps.reset();
		}

		void update() override
		{
			const float dt = getTimeStep() * 0.4f;

			ps.update(dt);
			ps.draw();

			// reset button
			if (isKeyPressedAndReleased(GLFW_KEY_R)) reset();
		}
	};
}

댓글