diff --git a/SQCSim2021/SQCSim2021.vcxproj b/SQCSim2021/SQCSim2021.vcxproj index 26260ba..cc39452 100644 --- a/SQCSim2021/SQCSim2021.vcxproj +++ b/SQCSim2021/SQCSim2021.vcxproj @@ -23,6 +23,7 @@ + @@ -43,6 +44,7 @@ + diff --git a/SQCSim2021/SQCSim2021.vcxproj.filters b/SQCSim2021/SQCSim2021.vcxproj.filters index 9a2c4a3..124abf1 100644 --- a/SQCSim2021/SQCSim2021.vcxproj.filters +++ b/SQCSim2021/SQCSim2021.vcxproj.filters @@ -71,6 +71,9 @@ Fichiers d%27en-tête + + Fichiers d%27en-tête + @@ -121,5 +124,8 @@ Fichiers sources + + Fichiers sources + \ No newline at end of file diff --git a/SQCSim2021/audio.cpp b/SQCSim2021/audio.cpp index 3a8b624..fcea86b 100644 --- a/SQCSim2021/audio.cpp +++ b/SQCSim2021/audio.cpp @@ -29,7 +29,7 @@ void Audio::Update3DAudio(Vector3f pos, Vector3f dir, Vector3f vel) { } void Audio::Create3DAudioObj(irrklang::ISound* sound, const char* name, Vector3f& pos, Vector3f& vel, float volume = 1) { - sound = m_engine->play3D(name, irrklang::vec3df(pos.x, pos.y, pos.z), true, false, true, irrklang::ESM_NO_STREAMING, true); + sound = m_engine->play3D(name, irrklang::vec3df(pos.x, pos.y, pos.z), false, false, true, irrklang::ESM_NO_STREAMING, true); sound->setVelocity(irrklang::vec3df(vel.x, vel.y, vel.z)); sound->setVolume(volume); } diff --git a/SQCSim2021/bullet.cpp b/SQCSim2021/bullet.cpp new file mode 100644 index 0000000..38f4901 --- /dev/null +++ b/SQCSim2021/bullet.cpp @@ -0,0 +1,32 @@ +#include "bullet.h" +#include "world.h" + +Bullet::Bullet(Player& player) { + m_startpos = m_currentpos = player.GetPOV() + player.GetDirection(); + m_velocity = player.GetDirection(); +} + +Bullet::~Bullet() {} + +bool Bullet::Update(World* world, Transformation& tran, float elapsedtime) { + for (int x = 0; x < 1000; ++x) { + m_currentpos += m_velocity * elapsedtime; + + if (!world->ChunkAt(m_currentpos)) + return true; + else if (world->BlockAt(m_currentpos) != BTYPE_AIR) { + world->ChangeBlockAtPosition(BTYPE_AIR, m_currentpos); + return true; + } + else if ((m_currentpos - m_startpos).Length() > VIEW_DISTANCE) return true; + } + + return false; +} + +void Bullet::Transpose(int& x, int& z) { + m_currentpos.x -= x * CHUNK_SIZE_X; + m_currentpos.z -= z * CHUNK_SIZE_Z; + m_startpos.x -= x * CHUNK_SIZE_X; + m_startpos.z -= z * CHUNK_SIZE_Z; +} diff --git a/SQCSim2021/bullet.h b/SQCSim2021/bullet.h new file mode 100644 index 0000000..ce706f8 --- /dev/null +++ b/SQCSim2021/bullet.h @@ -0,0 +1,27 @@ +#ifndef BULLET_H__ +#define BULLET_H__ + +#include "audio.h" +#include "player.h" +#include "vertexbuffer.h" +#include "texture.h" + +class World; + +class Bullet { +public: + Bullet(Player& player); + ~Bullet(); + + bool Update(World* world, Transformation& tran, float elapsedtime); + void Transpose(int& x, int& z); + +private: + Vector3f m_startpos; + Vector3f m_currentpos; + Vector3f m_velocity; + +}; + +#endif // BULLET_H__ + diff --git a/SQCSim2021/define.h b/SQCSim2021/define.h index 943cca2..a3f4c0d 100644 --- a/SQCSim2021/define.h +++ b/SQCSim2021/define.h @@ -27,6 +27,7 @@ #define VIEW_DISTANCE 128 #define TEXTURE_SIZE 32 +#define MAX_BULLETS 64 #endif #ifdef NDEBUG @@ -39,6 +40,7 @@ #define VIEW_DISTANCE 1024 #define TEXTURE_SIZE 512 +#define MAX_BULLETS 512 #endif diff --git a/SQCSim2021/engine.cpp b/SQCSim2021/engine.cpp index 7d3d913..9389499 100644 --- a/SQCSim2021/engine.cpp +++ b/SQCSim2021/engine.cpp @@ -8,7 +8,7 @@ Engine::Engine() { } Engine::~Engine() { m_world.CleanUpWorld(m_renderCount, true); - for (int x = 0; x < WORLD_SIZE_X; ++x) // Les destructeurs de Chunks ont de la misère, je les aide un peu! + for (int x = 0; x < WORLD_SIZE_X; ++x) for (int y = 0; y < WORLD_SIZE_Y; ++y) if (m_world.GetChunks().Get(x, y)) m_world.GetChunks().Get(x, y)->~Chunk(); @@ -45,6 +45,10 @@ void Engine::Init() { // Objet de musique! m_audio.ToggleMusicState(); + // Array pour les balles. + for (int x = 0; x < MAX_BULLETS; ++x) + m_bullets[x] = nullptr; + // Init Chunks m_world.GetChunks().Reset(nullptr); @@ -93,7 +97,7 @@ void Engine::LoadResource() { void Engine::UnloadResource() {} -void Engine::DrawHud(float elapsedTime) { +void Engine::DrawHud(float elapsedTime, BlockType bloc) { // Setter le blend function , tout ce qui sera noir sera transparent glDisable(GL_STENCIL_TEST); glColor4f(1.f, 1.f, 1.f, 1.f); @@ -124,6 +128,12 @@ void Engine::DrawHud(float elapsedTime) { ss.str(""); ss << " Position : " << m_player.GetPosition(); PrintText(10, 30, ss.str()); + ss.str(""); + ss << " Block : "; + if (bloc == BTYPE_LAST) + ss << "Weapon."; + else ss << (int)bloc; + PrintText(10, 40, ss.str()); m_textureCrosshair.Bind(); static const int crossSize = 32; glLoadIdentity(); @@ -173,19 +183,26 @@ int Engine::GetFps(float elapsedTime) const { return 1 / elapsedTime; } void Engine::Render(float elapsedTime) { static float gameTime = elapsedTime; + static float bulletTime = 0; + static BlockType bloc = 1; + if (elapsedTime > 0.1f) return; gameTime += elapsedTime; Transformation all; Transformation skybox; + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Transformations initiales glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - m_player.ApplyPhysics(m_player.GetInput(m_keyW, m_keyS, m_keyA, m_keyD, m_keySpace, m_keylshift, elapsedTime), m_world, elapsedTime); + if (bulletTime > 0.f) bulletTime -= elapsedTime; + if (bulletTime < 0.f) bulletTime = 0.f; + + m_player.ApplyPhysics(m_player.GetInput(m_keyW, m_keyS, m_keyA, m_keyD, m_keySpace, (bloc == BTYPE_LAST && bulletTime <= 0.f && m_mouseL), elapsedTime), m_world, elapsedTime, &m_audio); m_audio.Update3DAudio(m_player.GetPOV(), m_player.GetDirection(), m_player.GetVelocity()); // Ajustement du positionnement 3D avec les coordonnées du joueur et // son vecteur de vélocité (pour l'effet Doppler) m_player.ApplyTransformation(all); @@ -193,34 +210,64 @@ void Engine::Render(float elapsedTime) { m_player.ApplyTransformation(skybox, false); // Version d'ApplyTransformation qui ne tient compte que de la rotation // (donc l'objet ne bouge pas relativement au joueur, ce qui est pratique pour une skybox!). - static BlockType bloc = 1; - if (m_mouseWU) bloc++; else if (m_mouseWD) bloc--; - if (bloc == BTYPE_LAST) - bloc = BTYPE_DIRT; - else if (bloc == BTYPE_AIR) - bloc = BTYPE_LAST - 1; - + if (bloc == BTYPE_LAST + 1) bloc = BTYPE_AIR + 1; + else if (bloc == BTYPE_AIR) bloc = BTYPE_LAST; // La selection de BTYPE_LAST équipe l'arme. m_mouseWU = m_mouseWD = false; - if (m_mouseL) - m_world.ChangeBlockAtCursor(bloc, m_player, m_block); + if (m_mouseL) { + if (bloc != BTYPE_LAST) + m_world.ChangeBlockAtCursor(bloc, m_player, m_block); + else if (bulletTime <= 0.f) { + for (int x = 0; x < MAX_BULLETS; ++x) // Ajouter une balle dans l'array (aussi connu sous le nom de "faire pow pow"). + if (!m_bullets[x]) { + m_bullets[x] = new Bullet(m_player); + break; + } + else if (x == MAX_BULLETS - 1) { // S'il y a pas d'espace dans l'array, prendre la place de la première balle de l'array. + m_bullets[0]->~Bullet(); + m_bullets[0] = new Bullet(m_player); + } + bulletTime = .1f; + m_audio.Create3DAudioObj(m_powpow, AUDIO_PATH "pow.wav", m_player.GetPOV(), m_player.GetDirection() * 10, .5f); + if (m_flash) { // Coupe le rendering et affiche un frame blanc, pour simuler un flash. + glClearColor(.8f, .8f, .8f, 1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + glClearColor(0.f, 0.f, 0.f, 1.f); + return; + } + } + } else if (m_mouseR) m_world.ChangeBlockAtCursor(BTYPE_AIR, m_player, m_block); - m_world.Update(m_renderCount, m_badHitCount, m_player, all, m_shader01, m_textureAtlas, m_perlin, m_blockinfo); + for (int x = 0; x < MAX_BULLETS; ++x) // Array de bullets en jeu. + if (m_bullets[x]) + if (m_bullets[x]->Update(&m_world, all, elapsedTime)) { + m_bullets[x]->~Bullet(); + m_bullets[x] = nullptr; + } + + m_world.Update(m_renderCount, m_bullets, m_player, all, m_shader01, m_textureAtlas, m_perlin, m_blockinfo); if (m_isSkybox) m_skybox.Render(skybox); if (m_wireframe) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - DrawHud(elapsedTime); + DrawHud(elapsedTime, bloc); if (m_wireframe) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - if (m_player.GetPosition().y < -20.f) + static bool fell = false; + if (m_player.GetPosition().y < 1.7f && !fell) { + m_audio.Create3DAudioObj(m_scream, AUDIO_PATH "scream.wav", m_player.GetPOV(), m_player.GetVelocity(), 1.f); + fell = true; + } + else if (m_player.GetPosition().y < -20.f) { m_player = Player(Vector3f(0, CHUNK_SIZE_Y + 1.8f, 0)); // Respawn si le bonho- joueur tombe en bas du monde. + fell = false; + } } void Engine::KeyPressEvent(unsigned char key) { @@ -267,6 +314,7 @@ void Engine::KeyPressEvent(unsigned char key) { m_keySpace = true; } break; + case 5: // F - Ignorer case 24: // Y - Ignorer case 255: // Fn - Ignorer case 12: // M - Ignorer @@ -279,6 +327,9 @@ void Engine::KeyPressEvent(unsigned char key) { void Engine::KeyReleaseEvent(unsigned char key) { switch (key) { + case 5: + m_flash = !m_flash; + break; case 12: m_audio.ToggleMusicState(); break; diff --git a/SQCSim2021/engine.h b/SQCSim2021/engine.h index e1e4626..6b0d622 100644 --- a/SQCSim2021/engine.h +++ b/SQCSim2021/engine.h @@ -14,6 +14,7 @@ #include "array2d.h" #include "world.h" #include "perlin.h" +#include "bullet.h" class Engine : public OpenglContext { public: @@ -32,7 +33,7 @@ public: private: bool LoadTexture(Texture& texture, const std::string& filename, bool stopOnError = true); - void DrawHud(float elapsedTime); + void DrawHud(float elapsedTime, BlockType bloc); void PrintText(unsigned int x, unsigned int y, const std::string& t); int GetFps(float elapsedTime) const; @@ -56,8 +57,14 @@ private: Skybox m_skybox; Audio m_audio = Audio(AUDIO_PATH "music01.wav"); + irrklang::ISound* m_powpow; + irrklang::ISound* m_step; + irrklang::ISound* m_scream; + Player m_player = Player(Vector3f(0, CHUNK_SIZE_Y + 1.8f, 0)); + Bullet* m_bullets[MAX_BULLETS]; + bool m_keyW = false; bool m_keyA = false; bool m_keyS = false; @@ -71,6 +78,7 @@ private: bool m_mouseWD = false; bool m_block = false; + bool m_flash = true; }; diff --git a/SQCSim2021/media/audio/hit.wav b/SQCSim2021/media/audio/hit.wav new file mode 100644 index 0000000..57311e3 Binary files /dev/null and b/SQCSim2021/media/audio/hit.wav differ diff --git a/SQCSim2021/media/audio/pow.wav b/SQCSim2021/media/audio/pow.wav new file mode 100644 index 0000000..217373e Binary files /dev/null and b/SQCSim2021/media/audio/pow.wav differ diff --git a/SQCSim2021/media/audio/scream.wav b/SQCSim2021/media/audio/scream.wav new file mode 100644 index 0000000..08e6bc8 Binary files /dev/null and b/SQCSim2021/media/audio/scream.wav differ diff --git a/SQCSim2021/media/audio/step.wav b/SQCSim2021/media/audio/step.wav new file mode 100644 index 0000000..8e00ec3 Binary files /dev/null and b/SQCSim2021/media/audio/step.wav differ diff --git a/SQCSim2021/player.cpp b/SQCSim2021/player.cpp index 3c1ac4a..13b080a 100644 --- a/SQCSim2021/player.cpp +++ b/SQCSim2021/player.cpp @@ -18,7 +18,7 @@ void Player::TurnTopBottom(float value) { else if (m_rotX < -80) m_rotX = -80; } -Vector3f Player::GetInput(bool front, bool back, bool left, bool right, bool jump, bool dash, float elapsedTime) { +Vector3f Player::GetInput(bool front, bool back, bool left, bool right, bool jump, bool shoot, float elapsedTime) { Vector3f delta = Vector3f(0, 0, 0); @@ -53,16 +53,20 @@ Vector3f Player::GetInput(bool front, bool back, bool left, bool right, bool jum delta.x *= .6f; delta.z *= .6f; - if (jump && !m_airborne) { - delta.y += .32f; + if ((jump || shoot ) && !m_airborne) { + delta.y += jump? .32f: shoot? .1f : 0.f; m_airborne = true; } + if (shoot) // Recoil! + TurnTopBottom(-1); + return delta; } -void Player::ApplyPhysics(Vector3f input, World world, float elapsedTime) { - +void Player::ApplyPhysics(Vector3f input, World world, float elapsedTime, Audio* audio) { + static irrklang::ISound* step; // Pour les sons de pas. + static float timing = 0.f; /* Gestion de collisions */ BlockType bt1, bt2, bt3; @@ -81,6 +85,10 @@ void Player::ApplyPhysics(Vector3f input, World world, float elapsedTime) { bt1 = world.BlockAt(GetPosition().x, GetPosition().y + .3f, GetPosition().z); if (bt3 != BTYPE_AIR) { m_velocity.y = 0; + if (timing == 0.f) { + if (m_airborne) audio->Create3DAudioObj(step, AUDIO_PATH "hit.wav", GetPosition(), GetVelocity(), 1.f); + timing = .3f; + } m_airborne = false; } else if (bt1 != BTYPE_AIR) { @@ -89,6 +97,9 @@ void Player::ApplyPhysics(Vector3f input, World world, float elapsedTime) { else m_airborne = true; } + if (timing > 0.f) timing -= elapsedTime; + if (timing < 0.f) timing = 0.f; + bt1 = world.BlockAt(GetPosition().x + input.x, GetPosition().y, GetPosition().z); bt2 = world.BlockAt(GetPosition().x + input.x, GetPosition().y - 0.9f, GetPosition().z); bt3 = world.BlockAt(GetPosition().x + input.x, GetPosition().y - 1.7f, GetPosition().z); @@ -137,7 +148,22 @@ void Player::ApplyPhysics(Vector3f input, World world, float elapsedTime) { m_position += m_velocity; static float bobbingtime = 0; // Gestion de la caméra - if (bobbingtime <= 360.f) bobbingtime += elapsedTime * 20.f; else bobbingtime = 0; + static bool leftright = false; + static bool isStep = false; + if (bobbingtime <= 360.f) + bobbingtime += elapsedTime * 20.f; else bobbingtime = 0; + + if ((sin(bobbingtime) - 0.5f) * (abs(m_velocity.x) + abs(m_velocity.z)) < -.2f && !m_airborne) { + Vector3f vstep; + if (leftright) + vstep = Vector3f(GetPosition().x - GetDirection().x, GetPosition().y, GetPosition().z + GetDirection().z); + else vstep = Vector3f(GetPosition().x + GetDirection().x, GetPosition().y, GetPosition().z - GetDirection().z); + if (!isStep) { + audio->Create3DAudioObj(step, AUDIO_PATH "step.wav", vstep, GetVelocity(), .8f); + } + isStep = true; + } + else isStep = false; m_POV = m_position.y; m_POV += m_airborne ? 0 : (sin(bobbingtime) - 0.5f) * (abs(m_velocity.x) + abs(m_velocity.z)) * .2f; } diff --git a/SQCSim2021/player.h b/SQCSim2021/player.h index 8d6253f..efee2d7 100644 --- a/SQCSim2021/player.h +++ b/SQCSim2021/player.h @@ -2,6 +2,7 @@ #define _PLAYER_H__ #include "vector3.h" #include "transformation.h" +#include "audio.h" #include class World; @@ -12,7 +13,7 @@ public: void TurnLeftRight(float value); void TurnTopBottom(float value); Vector3f GetInput(bool front, bool back, bool left, bool right, bool jump, bool dash, float elapsedTime); - void ApplyPhysics(Vector3f input, World world, float elapsedTime); + void ApplyPhysics(Vector3f input, World world, float elapsedTime, Audio* audio); void ApplyTransformation(Transformation& transformation, bool rel = true) const; Vector3f GetPosition() const; diff --git a/SQCSim2021/world.cpp b/SQCSim2021/world.cpp index c3ead37..272ea8f 100644 --- a/SQCSim2021/world.cpp +++ b/SQCSim2021/world.cpp @@ -38,7 +38,7 @@ BlockType World::BlockAt(const Vector3f& pos, BlockType defaultBlockType) const return BlockAt(pos.x, pos.y, pos.z, defaultBlockType); } -void World::TransposeWorld(Player& player) { +void World::TransposeWorld(Player& player, Bullet* bullets[MAX_BULLETS]) { int x = 0; int y = 0; @@ -85,6 +85,9 @@ void World::TransposeWorld(Player& player) { m_center[0] += x; m_center[1] += y; player.Teleport(x, y); + + for (int index = 0; index < MAX_BULLETS; ++index) + if (bullets[index]) bullets[index]->Transpose(x, y); } void World::CleanUpWorld(int& deleteframes, bool clear = false) { @@ -106,12 +109,12 @@ void World::GetScope(int& x, int& y) { y = m_center[1]; } -void World::Update(int& rendercount, int& badhitcount, Player& player, Transformation& world, Shader& shader, TextureAtlas& atlas, Perlin& perlin, BlockInfo* blockinfo[BTYPE_LAST]) { +void World::Update(int& rendercount, Bullet* bullets[MAX_BULLETS], Player& player, Transformation& world, Shader& shader, TextureAtlas& atlas, Perlin& perlin, BlockInfo* blockinfo[BTYPE_LAST]) { glStencilFunc(GL_EQUAL, 1, 0x00); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); atlas.Bind(); - RenderWorld(rendercount, badhitcount, player, world, shader); - TransposeWorld(player); + RenderWorld(rendercount, player, world, shader); + TransposeWorld(player, bullets); UpdateWorld(player, perlin, blockinfo); shader.Disable(); glStencilFunc(GL_GREATER, 1, 0xFF); @@ -273,10 +276,19 @@ void World::ChangeBlockAtCursor(BlockType blockType, Player& player, bool& block } } -void World::RenderWorld(int& rendercount, int& badhitcount, Player& player, Transformation& world, Shader& shader) { + +void World::ChangeBlockAtPosition(BlockType blockType, Vector3f pos) { + int bx = (int)pos.x % CHUNK_SIZE_X; + int by = (int)pos.y % CHUNK_SIZE_Y; + int bz = (int)pos.z % CHUNK_SIZE_Z; + + ChunkAt(pos)->SetBlock(bx, by, bz, blockType, this); + ChunkAt(pos)->MakeModified(); +} + +void World::RenderWorld(int& rendercount, Player& player, Transformation& world, Shader& shader) { shader.Use(); rendercount = 0; - badhitcount = 0; Vector3f angle; Vector3f cursor; Vector3f direct = player.GetDirection(); @@ -341,10 +353,8 @@ void World::RenderWorld(int& rendercount, int& badhitcount, Player& player, Tran int chx, chy; ChunkAt(cursor)->GetPosition(chx, chy); for (int index = 0; index < rendercount; ++index) // Permet de vérifier seulement contre celles ajoutées dans la frame, et ne pas avoir à refaire l'array à chaque frame. - if (renderManifest[index].x == chx && renderManifest[index].z == chy) { + if (renderManifest[index].x == chx && renderManifest[index].z == chy) valide = false; - ++badhitcount; - } if (valide) renderManifest[rendercount++] = Vector3f(chx, (VIEW_DISTANCE - (pos - cursor).Length() * 2.f + 128) / (float)VIEW_DISTANCE, chy); } diff --git a/SQCSim2021/world.h b/SQCSim2021/world.h index db8cc16..f181865 100644 --- a/SQCSim2021/world.h +++ b/SQCSim2021/world.h @@ -8,6 +8,7 @@ #include "transformation.h" #include "perlin.h" #include "shader.h" +#include "bullet.h" #include "textureatlas.h" #include #include @@ -15,6 +16,7 @@ class Chunk; class Player; +class Bullet; class World { public: @@ -29,11 +31,12 @@ public: BlockType BlockAt(float x, float y, float z, BlockType defaultBlockType = BTYPE_AIR) const; BlockType BlockAt(const Vector3f& pos, BlockType defaultBlockType = BTYPE_AIR) const; - void Update(int& rendercount, int& badhitcount, Player& player, Transformation& world, Shader& shader, TextureAtlas& atlas, Perlin& perlin, BlockInfo* blockinfo[BTYPE_LAST]); + void Update(int& rendercount, Bullet* bullets[MAX_BULLETS], Player& player, Transformation& world, Shader& shader, TextureAtlas& atlas, Perlin& perlin, BlockInfo* blockinfo[BTYPE_LAST]); void GetScope(int& x, int& y); void ChangeBlockAtCursor(BlockType blockType, Player& player, bool& block); + void ChangeBlockAtPosition(BlockType blockType, Vector3f pos); void CleanUpWorld(int& deleteframes, bool clear); private: Array2d m_chunks = Array2d(WORLD_SIZE_X, WORLD_SIZE_Y); @@ -43,9 +46,9 @@ private: bool GenerateChunk(int x, int y, Perlin& perlin); void UpdateChunk(int& generates, int& updates, int chx, int chy, Perlin& perlin, BlockInfo* blockinfo[BTYPE_LAST]); - void RenderWorld(int& rendercount, int& badhitcount, Player& player, Transformation& world, Shader& shader); + void RenderWorld(int& rendercount, Player& player, Transformation& world, Shader& shader); void UpdateWorld(Player& player, Perlin& perlin, BlockInfo* blockinfo[BTYPE_LAST]); - void TransposeWorld(Player& player); + void TransposeWorld(Player& player, Bullet* bullets[MAX_BULLETS]); }; #endif // WORLD_H__