finished basic spaceship, it can move and shoot

This commit is contained in:
VisualGMQ 2022-02-07 21:38:35 +08:00
parent d1fbdb1898
commit 71096200f6
47 changed files with 684 additions and 35 deletions

View File

@ -25,8 +25,7 @@ add_subdirectory(libs/glfw)
target_include_directories(
${ENGINE_NAME}
PRIVATE include/tinyengine
PUBLIC libs/glad/include libs/stb_image libs/miniaudio # /usr/local/Cellar/glm/0.9.9.8/include
PUBLIC include libs/glad/include libs/stb_image libs/miniaudio # /usr/local/Cellar/glm/0.9.9.8/include
)
target_link_libraries(
@ -54,13 +53,9 @@ target_compile_options(
################
aux_source_directory(src/game GAME_SRC)
aux_source_directory(src/game/stages GAME_SRC)
aux_source_directory(src/game/controllers GAME_SRC)
add_executable(${PROJECT_NAME} ${GAME_SRC})
target_include_directories(
${PROJECT_NAME}
PRIVATE include
)
target_link_libraries(
${PROJECT_NAME}
PRIVATE ${ENGINE_NAME}

BIN
assets/shoot.wav Normal file

Binary file not shown.

BIN
assets/tilesheet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

7
include/game/action.hpp Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "tinyengine/tinyengine.hpp"
#include "game/component.hpp"
#include "game/entity.hpp"
void Shoot(SpaceshipWeaponCmpt& weapon, const Point& dir);

125
include/game/component.hpp Normal file
View File

@ -0,0 +1,125 @@
#pragma once
#include "tinyengine/tinyengine.hpp"
#include "game/constants.hpp"
class MoveCmpt: public Component {
public:
void Init(const Point& p) { position = p; }
void Release() {}
Point position;
Point oldPosition = {0, 0};
};
class MotionCmpt: public Component {
public:
void Init(const Point& spd, float maxSpd) {
speed = spd;
maxSpeed = maxSpd;
}
void Release() {}
Point speed;
Point acceleration = {0, 0};
float maxSpeed;
};
class CollisionCmpt: public Component {
public:
void Init(const Size& size) { rect.w = size.w; rect.h = size.h; }
void Release() {}
Rect rect;
};
class SpaceshipWeaponCmpt: public Component {
public:
enum Type {
Orientation = 0x01,
FreeRotation = 0x02,
};
void Init(Type type,
Entity* owner,
int damage,
float shootSpeed,
float maxSpeed,
float duration) {
this->type = type;
this->owner = owner;
this->damage = damage;
this->shootSpeed = shootSpeed;
this->shootDuration = duration;
this->maxSpeed = maxSpeed;
}
void Release() {}
bool IsCoolDowning() const { return coolDown >= 0; }
Entity* ShootBullet(const Point& dir);
Type type;
int damage;
float shootSpeed;
float shootDuration;
float coolDown;
float maxSpeed;
Entity* owner;
};
class SpaceshipArmorCmpt: public Component {
public:
void Init(int defence, float duration) {
this->defence = defence;
this->recoverDuration = duration;
}
int defence;
float recoverDuration;
};
class LifeCmpt: public Component {
public:
void Init(int hp) { this->hp = hp; }
void Release() {}
int hp;
};
class RenderCmpt: public Component {
public:
enum Type {
TypeTexture = 1,
TypeTile,
};
void Init(Texture* image) {
texture = image;
type = TypeTexture;
}
void Init(const Tile& tile) {
this->tile = tile;
type = TypeTile;
}
void Release() {}
Type type;
union {
Texture* texture;
Tile tile;
};
};
class BulletCmpt: public Component {
public:
inline void Init(int damage, Entity* owner) {
this->owner = owner;
this->damage = damage;
}
inline void Release() override {}
int damage;
Entity* owner;
bool alive;
};

View File

@ -0,0 +1,9 @@
#pragma once
constexpr float FreightMaxSpeed = 100;
constexpr float LazerDamage = 2;
constexpr float LazerShooterSpeed = 1000;
constexpr float LazerShooterMaxSpeed = 1000;
constexpr float LazerShooterCooldown = 0.3;
constexpr int GameWindowWidth = 1024;
constexpr int GameWindowHeight = 720;

View File

@ -0,0 +1,8 @@
#pragma once
class Controller {
public:
virtual ~Controller() = default;
virtual void Update(float dt) = 0;
};

View File

@ -0,0 +1,16 @@
#pragma once
#include "game/controller.hpp"
#include "tinyengine/tinyengine.hpp"
#include "game/entity.hpp"
#include "game/component.hpp"
#include "game/action.hpp"
class FreeRotationController: public Controller {
public:
FreeRotationController(Entity* entity);
void Update(float dt) override;
private:
Entity* entity_;
};

8
include/game/entity.hpp Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include "tinyengine/tinyengine.hpp"
#include "game/component.hpp"
#include "game/global.hpp"
Entity* CreateFreightShip();
Entity* CreateBullet(int damage, Entity* owner, float maxSpeedß);

18
include/game/global.hpp Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include "tinyengine/tinyengine.hpp"
#include "game/quick_list.hpp"
extern Context ECSContext;
extern SystemManager SystemMgr;
extern std::unordered_map<std::string, Unique<TileSheet>> TileSheets;
extern std::unordered_map<std::string, Unique<Sound>> Sounds;
extern std::string TileSheetName;
extern QuickList<Entity*> Bullets;
using EntityList = std::vector<Entity*>;
extern EntityList Entities;
void LoadResources();

View File

@ -0,0 +1,45 @@
#pragma once
#include "tinyengine/tinyengine.hpp"
template <typename T>
class QuickList final {
public:
void Add(const T& elem) {
datas_.push_back(elem);
}
void RemoveAll(std::function<bool(const T&)> func) {
int idx = 0;
while (idx < datas_.size()) {
if (func(datas_[idx]) && datas_.size() > idx) {
std::swap(datas_[idx], datas_[datas_.size() - 1]);
datas_.pop_back();
} else {
idx ++;
}
}
}
void Clear() { datas_.clear(); }
// for debug
void PrintAll(std::function<void(const T&)> func = nullptr) {
printf("QuickList: ");
for (auto& elem : datas_) {
if (func == nullptr) {
std::cout << elem << ", ";
} else {
func(elem);
}
}
printf("\n");
}
using const_iterator = typename std::vector<T>::const_iterator;
const_iterator begin() const { return datas_.begin(); }
const_iterator end() const { return datas_.end(); }
private:
std::vector<T> datas_;
};

View File

@ -0,0 +1,19 @@
#pragma once
#include "tinyengine/tinyengine.hpp"
#include "game/global.hpp"
#include "game/entity.hpp"
#include "game/system.hpp"
#include "game/controllers/spaceship_controller.hpp"
class SpaceScence: public Scence {
public:
void OnInit() override;
void OnUpdate(float dt) override;
void OnRender() override;
void OnQuit() override;
private:
Entity* spaceship_;
Unique<FreeRotationController> controller_;
};

41
include/game/system.hpp Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#include "tinyengine/ecs/system.hpp"
#include "tinyengine/tinyengine.hpp"
#include "game/global.hpp"
#include "game/component.hpp"
#include "game/entity.hpp"
class PhysicalSystem: public UpdateSystem {
public:
void Update(float dt) override;
private:
void physicalStep(Entity* entity, float dt, MoveCmpt&, MotionCmpt&);
};
class ColliRectCorrectSystem: public UpdateSystem {
public:
void Update(float dt) override;
private:
void correctColliRect(MoveCmpt&, CollisionCmpt&);
};
class CleanupSystem: public UpdateSystem {
public:
void Update(float dt) override;
};
class BulletCooldownSystem: public UpdateSystem {
public:
void Update(float dt) override;
};
class RenderEntitySystem: public RenderSystem {
public:
void Render() override;
private:
void renderEntity(Entity* entity, const RenderCmpt&);
};

View File

@ -1,7 +1,7 @@
_Pragma("once")
#include "pch.hpp"
#include "pool.hpp"
#include "tinyengine/pch.hpp"
#include "tinyengine/pool.hpp"
using ComponentId = unsigned int;

View File

@ -1,6 +1,6 @@
_Pragma("once")
#include "pool.hpp"
#include "tinyengine/pool.hpp"
#include "component.hpp"
class Entity;

View File

@ -3,3 +3,4 @@ _Pragma("once")
#include "entity.hpp"
#include "component.hpp"
#include "context.hpp"
#include "system.hpp"

View File

@ -1,7 +1,7 @@
_Pragma("once")
#pragma once
#include "pch.hpp"
#include "pool.hpp"
#include "tinyengine/pch.hpp"
#include "tinyengine/pool.hpp"
#include "component.hpp"
#include "context.hpp"

View File

@ -0,0 +1,48 @@
#pragma once
#include "entity.hpp"
#include "tinyengine/tinyengine.hpp"
class System {
public:
virtual ~System() = default;
bool IsWorking() const { return active_; }
void StartWork() { active_ = true; }
void StopWork() { active_ = false; }
private:
bool active_ = true;
};
class UpdateSystem: public System {
public:
virtual ~UpdateSystem() = default;
virtual void Update(float dt) = 0;
};
class RenderSystem: public System {
public:
virtual ~RenderSystem() = default;
virtual void Render() = 0;
};
class SystemManager final {
public:
void AddUpdateSystem(UpdateSystem* system) { updateSystems_.emplace_back(system); }
void AddRenderSystem(RenderSystem* system) { renderSystems_.emplace_back(system); }
void Update(float dt);
void Render();
void Clear() { ClearUpdateSystems(); ClearRenderSystems(); }
void ClearUpdateSystems() { updateSystems_.clear(); }
void ClearRenderSystems() { renderSystems_.clear(); }
private:
std::vector<Ref<UpdateSystem>> updateSystems_;
std::vector<Ref<RenderSystem>> renderSystems_;
};

View File

@ -1,4 +1,5 @@
#pragma once
#include "pch.hpp"
#include "libmath.hpp"
#include "renderer.hpp"

View File

@ -2,3 +2,9 @@
#include "renderer.hpp"
void OnWindowResize(GLFWwindow* window, int width, int height);
bool IsKeyPressing(int key);
bool IsLeftPressing();
bool IsRightPressing();
Point GetMousePosition();

View File

@ -2,9 +2,10 @@
#include "glad/gl.h"
#include "GLFW/glfw3.h"
#include "stb_image.h"
#include <string>
#include <vector>
#include <list>
#include <iostream>
#include <memory>
#include <fstream>
@ -22,6 +23,7 @@
#include <cassert>
#include "miniaudio.h"
#include "stb_image.h"
#ifdef USE_GLM
#include "glm/glm.hpp"

View File

@ -11,3 +11,4 @@
#include "inner_bmpfont.hpp"
#include "audio.hpp"
#include "tilesheet.hpp"
#include "ecs/ecs.hpp"

14
src/game/action.cpp Normal file
View File

@ -0,0 +1,14 @@
#include "game/action.hpp"
void Shoot(SpaceshipWeaponCmpt& weapon, const Point& dir) {
if (weapon.IsCoolDowning()) return;
Point playerCenterPos = weapon.owner->Get<MoveCmpt>()->position;
auto bullet = weapon.ShootBullet(Normalize(dir) * weapon.shootSpeed);
Bullets.Add(bullet);
weapon.coolDown = weapon.shootDuration;
Sounds["shoot"]->Play();
}

9
src/game/component.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "game/component.hpp"
#include "game/entity.hpp"
Entity* SpaceshipWeaponCmpt::ShootBullet(const Point& dir) {
Entity* bullet = CreateBullet(damage, owner, maxSpeed);
bullet->Use<MotionCmpt>()->speed = Normalize(dir) * shootSpeed;
bullet->Use<MoveCmpt>()->position = owner->Get<MoveCmpt>()->position;
return bullet;
}

View File

@ -0,0 +1,37 @@
#include "game/controllers/spaceship_controller.hpp"
#include "tinyengine/event.hpp"
FreeRotationController::FreeRotationController(Entity* entity)
: entity_(entity) {}
void FreeRotationController::Update(float dt) {
const float spd = 100;
auto motionCmpt = entity_->Use<MotionCmpt>();
if (IsKeyPressing(GLFW_KEY_A)) {
// motionCmpt->speed += Point{-spd, 0};
motionCmpt->acceleration += Point{-spd, 0};
// motionCmpt->speed.x = -spd;
}
if (IsKeyPressing(GLFW_KEY_D)) {
// motionCmpt->speed += Point{spd, 0};
motionCmpt->acceleration += Point{spd, 0};
// motionCmpt->speed.x = spd;
}
if (IsKeyPressing(GLFW_KEY_S)) {
// motionCmpt->speed += Point{0, spd};
motionCmpt->acceleration += Point{0, spd};
// motionCmpt->speed.y = spd;
}
if (IsKeyPressing(GLFW_KEY_W)) {
// motionCmpt->speed += Point{0, -spd};
motionCmpt->acceleration += Point{0, -spd};
// motionCmpt->speed.y = -spd;
}
if (IsLeftPressing()) {
if (entity_->Has<SpaceshipWeaponCmpt>()) {
Shoot(*entity_->Use<SpaceshipWeaponCmpt>(),
GetMousePosition() - entity_->Get<MoveCmpt>()->position);
}
}
}

24
src/game/entity.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "game/entity.hpp"
Entity* CreateFreightShip() {
Entity* entity = ECSContext.CreateEntity();
entity->Add<MoveCmpt>(Point{0, 0});
entity->Add<MotionCmpt>(Point{0, 0}, FreightMaxSpeed);
entity->Add<RenderCmpt>(TileSheets[TileSheetName]->GetTile(0, 0));
entity->Add<SpaceshipWeaponCmpt>(SpaceshipWeaponCmpt::Orientation,
entity,
LazerDamage,
LazerShooterSpeed,
LazerShooterMaxSpeed,
LazerShooterCooldown);
return entity;
}
Entity* CreateBullet(int damage, Entity* owner, float maxSpeed) {
Entity* entity = ECSContext.CreateEntity();
entity->Add<MoveCmpt>(Point{0, 0});
entity->Add<MotionCmpt>(Point{0, 0}, maxSpeed);
entity->Add<RenderCmpt>(TileSheets[TileSheetName]->GetTile(0, 1));
entity->Add<BulletCmpt>(damage, owner);
return entity;
}

22
src/game/global.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "game/global.hpp"
Context ECSContext;
SystemManager SystemMgr;
std::unordered_map<std::string, Unique<TileSheet>> TileSheets;
std::unordered_map<std::string, Unique<Sound>> Sounds;
std::string TileSheetName = "TileSheet";
QuickList<Entity*> Bullets;
EntityList Entities;
void loadImages() {
TileSheets[TileSheetName] = std::make_unique<TileSheet>("assets/tilesheet.png", 8, 8);
}
void loadSounds() {
Sounds["shoot"] = std::make_unique<Sound>("assets/shoot.wav");
}
void LoadResources() {
loadImages();
loadSounds();
}

View File

@ -1,3 +1,5 @@
#include "game/stages/gamelogo.hpp"
#include "game/stages/space.hpp"
#include "game/constants.hpp"
RUN_WINDOW("Space Sector", 1024, 720, GameLogoScence)
RUN_WINDOW("Space Sector", GameWindowWidth, GameWindowHeight, SpaceScence)

32
src/game/stages/space.cpp Normal file
View File

@ -0,0 +1,32 @@
#include "game/stages/space.hpp"
void SpaceScence::OnInit() {
Renderer::SetClearColor(Color{0, 0, 0, 255});
LoadResources();
spaceship_ = CreateFreightShip();
Entities.push_back(spaceship_);
spaceship_->Use<MoveCmpt>()->position = Point{400, 400};
controller_.reset(new FreeRotationController(spaceship_));
SystemMgr.Clear();
SystemMgr.AddRenderSystem(new RenderEntitySystem);
SystemMgr.AddUpdateSystem(new BulletCooldownSystem);
SystemMgr.AddUpdateSystem(new PhysicalSystem);
SystemMgr.AddUpdateSystem(new ColliRectCorrectSystem);
SystemMgr.AddUpdateSystem(new CleanupSystem);
}
void SpaceScence::OnUpdate(float dt) {
controller_->Update(dt);
SystemMgr.Update(dt);
}
void SpaceScence::OnRender() {
SystemMgr.Render();
}
void SpaceScence::OnQuit() {
}

98
src/game/system.cpp Normal file
View File

@ -0,0 +1,98 @@
#include "game/system.hpp"
void PhysicalSystem::Update(float dt) {
for (auto& entity: Entities) {
if (entity->Has<MoveCmpt>() && entity->Has<MotionCmpt>()) {
// update entities
physicalStep(entity, dt,
*entity->Use<MoveCmpt>(),
*entity->Use<MotionCmpt>());
// update bullets
for (auto& bullet : Bullets) {
if (bullet->Has<MoveCmpt>() && bullet->Has<MotionCmpt>()) {
physicalStep(bullet, dt,
*bullet->Use<MoveCmpt>(),
*bullet->Use<MotionCmpt>());
}
}
}
}
}
void PhysicalSystem::physicalStep(Entity* entity,
float dt,
MoveCmpt& moveCmpt,
MotionCmpt& motionCmpt) {
moveCmpt.oldPosition = moveCmpt.position;
motionCmpt.speed += motionCmpt.acceleration * dt;
if (Len(motionCmpt.speed) > motionCmpt.maxSpeed) {
motionCmpt.speed = Normalize(motionCmpt.speed) * motionCmpt.maxSpeed;
}
moveCmpt.position += motionCmpt.speed * dt + 0.5 * motionCmpt.acceleration * dt * dt;
motionCmpt.acceleration = Point{0, 0};
}
void ColliRectCorrectSystem::Update(float dt) {
for (auto& entity: Entities) {
if (entity->Has<CollisionCmpt>() &&
entity->Has<MoveCmpt>()) {
correctColliRect(*entity->Use<MoveCmpt>(),
*entity->Use<CollisionCmpt>());
}
}
}
void ColliRectCorrectSystem::correctColliRect(MoveCmpt& move, CollisionCmpt& collision) {
collision.rect.x = move.position.x;
collision.rect.y = move.position.y;
}
void CleanupSystem::Update(float dt) {
using EntityPtr = Entity*;
Bullets.RemoveAll([](const EntityPtr& entity){
return !IsPointInRect(entity->Get<MoveCmpt>()->position,
Rect{0, 0, GameWindowWidth, GameWindowHeight});
});
}
void BulletCooldownSystem::Update(float dt) {
for (auto& entity : Entities) {
if (entity->Has<SpaceshipWeaponCmpt>()) {
auto weapon = entity->Use<SpaceshipWeaponCmpt>();
if (weapon->coolDown >= 0) {
weapon->coolDown -= dt;
}
}
}
}
void RenderEntitySystem::Render() {
for (auto& entity: Entities) {
if (entity->Has<RenderCmpt>()) {
renderEntity(entity, *entity->Get<RenderCmpt>());
}
}
for (auto& bullet: Bullets) {
renderEntity(bullet, *bullet->Get<RenderCmpt>());
}
}
void RenderEntitySystem::renderEntity(Entity* entity, const RenderCmpt& renderCmpt) {
if (entity->Has<MoveCmpt>()) {
auto& pos = entity->Get<MoveCmpt>()->position;
if (renderCmpt.type == RenderCmpt::TypeTexture) {
Renderer::DrawTexture(renderCmpt.texture,
nullptr,
pos);
} else {
Renderer::DrawTile(renderCmpt.tile,
pos);
}
}
}

View File

@ -1,4 +1,4 @@
#include "audio.hpp"
#include "tinyengine/audio.hpp"
ma_engine AudioEngine;

View File

@ -1,4 +1,4 @@
#include "camera.hpp"
#include "tinyengine/camera.hpp"
bool Camera::TryCalcView() {
if (dirty_) {

View File

@ -1,5 +1,5 @@
#include "ecs/component.hpp"
#include "ecs/context.hpp"
#include "tinyengine/ecs/component.hpp"
#include "tinyengine/ecs/context.hpp"
ComponentId ComponentIdx::count_ = 0;

View File

@ -1,5 +1,5 @@
#include "ecs/context.hpp"
#include "ecs/entity.hpp"
#include "tinyengine/ecs/context.hpp"
#include "tinyengine/ecs/entity.hpp"
Pool<Entity>::ElemContainer& Context::GetEntities() {
return entities_.GetElems();

View File

@ -0,0 +1,17 @@
#include "tinyengine/ecs/system.hpp"
void SystemManager::Update(float dt) {
for (auto& system: updateSystems_) {
if (system->IsWorking()) {
system->Update(dt);
}
}
}
void SystemManager::Render() {
for (auto& system: renderSystems_) {
if (system->IsWorking()) {
system->Render();
}
}
}

View File

@ -1,4 +1,4 @@
#include "engine.hpp"
#include "tinyengine/engine.hpp"
void error_callback(int error, const char* description) {
fprintf(stderr, "Error: %s\n", description);

View File

@ -1,5 +1,24 @@
#include "event.hpp"
#include "tinyengine/event.hpp"
#include "tinyengine/engine.hpp"
void OnWindowResize(GLFWwindow* window, int width, int height) {
Renderer::SetViewport(0, 0, width, height);
}
bool IsKeyPressing(int key) {
return glfwGetKey(engine.GetWindow(), key) == GLFW_PRESS;
}
bool IsLeftPressing() {
return glfwGetMouseButton(engine.GetWindow(), GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS;
}
bool IsRightPressing() {
return glfwGetMouseButton(engine.GetWindow(), GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS;
}
Point GetMousePosition() {
double x, y;
glfwGetCursorPos(engine.GetWindow(), &x, &y);
return Point{float(x), float(y)};
}

View File

@ -1,4 +1,4 @@
#include "glhelpfunc.hpp"
#include "tinyengine/glhelpfunc.hpp"
#define CASE(e) case e: return #e;

View File

@ -1,4 +1,4 @@
#include "inner_bmpfont.hpp"
#include "tinyengine/inner_bmpfont.hpp"
InnerBmpFont::InnerBmpFont() {
saveChar('0',

View File

@ -1,4 +1,4 @@
#include "libmath.hpp"
#include "tinyengine/libmath.hpp"
Point operator+(const Point& p1, const Point& p2) {
return Point{p1.x + p2.x, p1.y + p2.y};

View File

@ -1,5 +1,5 @@
#include "renderer.hpp"
#include "engine.hpp"
#include "tinyengine/renderer.hpp"
#include "tinyengine/engine.hpp"
Color Renderer::color_ = {255, 255, 255, 255};

View File

@ -1,4 +1,4 @@
#include "shader.hpp"
#include "tinyengine/shader.hpp"
GLuint createShader(const std::string& code, GLenum type) {
GLuint shader;

View File

@ -1,4 +1,4 @@
#include "texture.hpp"
#include "tinyengine/texture.hpp"
Texture::Texture(const std::string& filename) {
int w, h, channels;

View File

@ -1,4 +1,4 @@
#include "tilesheet.hpp"
#include "tinyengine/tilesheet.hpp"
TileSheet::TileSheet(const std::string& filename, int x, int y) {
texture_.reset(new Texture(filename));

View File

@ -10,3 +10,4 @@ AddTest(math math.cpp ${ENGINE_NAME})
AddTest(renderer renderer.cpp ${ENGINE_NAME})
AddTest(innerBmpFont innerBmpFont.cpp ${ENGINE_NAME})
AddTest(audio audio.cpp ${ENGINE_NAME})
AddTest(quicklist quicklist.cpp ${ENGINE_NAME})

24
tests/quicklist.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "game/quick_list.hpp"
int main() {
QuickList<int> list;
list.Add(1);
list.Add(3);
list.Add(2);
list.Add(5);
list.PrintAll();
list.RemoveAll([](const int& elem){
return elem < 3;
});
list.PrintAll();
list.RemoveAll([](const int& elem){
return elem < 10;
});
list.PrintAll();
}