add enemy collision

This commit is contained in:
Cocktail Frank 2023-06-26 21:08:43 -04:00
parent 21ee780fab
commit 3c67b92361
8 changed files with 191 additions and 51 deletions

View File

@ -54,8 +54,7 @@ $(GLEW_DIR)%.o: $(GLEW_DIR)%.c $(GLEW_DIR)%.h
$(SB_SRC_DIR)extension.o : $(addprefix $(SB_SRC_DIR),Box.hpp Segment.hpp Color.hpp filesystem.hpp Pixels.hpp Log.hpp)
$(SB_SRC_DIR)Node.o : $(addprefix $(SB_SRC_DIR),Game.hpp Configuration.hpp Delegate.hpp Display.hpp Input.hpp Box.hpp Audio.hpp Log.hpp)
$(SB_SRC_DIR)Game.o : $(addprefix $(SB_SRC_DIR),extension.hpp Node.hpp Recorder.hpp Input.hpp Configuration.hpp \
Delegate.hpp Audio.hpp Log.hpp)
$(SB_SRC_DIR)Game.o : $(addprefix $(SB_SRC_DIR),extension.hpp Node.hpp Recorder.hpp Input.hpp Configuration.hpp Delegate.hpp Audio.hpp Log.hpp)
$(SB_SRC_DIR)Animation.o : $(addprefix $(SB_SRC_DIR),Node.hpp Timer.hpp)
$(SB_SRC_DIR)Recorder.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Configuration.hpp Delegate.hpp Animation.hpp extension.hpp)
$(SB_SRC_DIR)Input.o : $(addprefix $(SB_SRC_DIR),Node.hpp Animation.hpp Configuration.hpp Delegate.hpp)
@ -76,7 +75,7 @@ $(SRC_DIR)Curve.o : $(addprefix $(SB_SRC_DIR),Attributes.hpp math.hpp extension.
$(SRC_DIR)Character.o : $(addprefix $(SB_SRC_DIR),Configuration.hpp Switch.hpp Selection.hpp Segment.hpp Timer.hpp) $(addprefix $(SRC_DIR),Sprite.hpp Curve.hpp)
$(SRC_DIR)Pad.o : $(addprefix $(SRC_DIR),Model.hpp Switch.hpp)
$(SRC_DIR)Sprite.o : $(addprefix $(SRC_DIR),Model.hpp)
$(SRC_DIR)Enemy.o : $(addprefix $(SB_SRC_DIR),Timer.hpp Animation.hpp) $(addprefix $(SRC_DIR),Sprite.hpp Curve.hpp)
$(SRC_DIR)Enemy.o : $(addprefix $(SB_SRC_DIR),Timer.hpp Animation.hpp Box.hpp) $(addprefix $(SRC_DIR),Sprite.hpp Curve.hpp Character.hpp)
$(SRC_DIR)Cakefoot.o : $(SRC_H_FILES) $(SB_H_FILES)
%.o : %.cpp %.hpp
$(CXX) $(CXXFLAGS) $< -c -o $@

2
lib/sb

@ -1 +1 @@
Subproject commit c0b55752e1660c41feb841ce059bf893c8d773a2
Subproject commit 12e5a15d1ced403cedf23752bea487a06e37d4f4

View File

@ -87,9 +87,9 @@ Cakefoot::Cakefoot()
poke = std::shared_ptr<SDL_Cursor>(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND), SDL_FreeCursor);
grab = std::shared_ptr<SDL_Cursor>(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL), SDL_FreeCursor);
/* Initialize character sprite and position */
character.sprite = Sprite("resource/cake-frames/cake1.png", glm::vec2{20.0f / 486.0f});
character.reset(curve());
/* Load title screen and character graphics */
character.load();
load_level(0);
}
void Cakefoot::load_gl_context()
@ -172,7 +172,7 @@ void Cakefoot::load_level(int index)
{
level_index = index;
curve_index = index;
character.reset(curve());
character.beginning(curve());
if (index == 1)
{
enemies = {
@ -380,11 +380,16 @@ void Cakefoot::update(float timestamp)
set_framerate(configuration()["display"]["framerate"]);
glUniform1f(uniform["time"], timer.elapsed());
/* Update character, along the curve, using the timer to determine movement since last frame, and update enemies. */
/* Update character, along the curve, using the timer to determine movement since last frame, and update enemies. Check for collison
* as enemies are updated. */
character.update(curve(), timer);
for (auto& enemy : enemies)
{
enemy->update(timer);
if (enemy->collide(character.box(), character.sprite(), {-curve().aspect, -1.0f, -1.0f}, {curve().aspect, 1.0f, 1.0f}))
{
character.beginning(curve());
}
}
/* Transformation for rotating the model space and looking at the center of the field of play from the camera position. */
@ -394,9 +399,6 @@ void Cakefoot::update(float timestamp)
/* Transformation from camera space to clip space. */
projection = glm::perspective(field_of_view_y, window_box().aspect(), 0.1f, 100.0f);
/* Cake coordinates wrapped */
glm::vec2 cake_translation = sb::wrap_point(character.position, {-curve().aspect, -1.0f, 0.0f}, {curve().aspect, 1.0f, 1.0f});
/* Plane position vertices will be used for everything before the curve */
sb::Plane::position->bind("vertex_position", shader_program);
@ -436,9 +438,7 @@ void Cakefoot::update(float timestamp)
}
/* Draw cake */
character.sprite.translate(glm::vec3{cake_translation, 0.0f});
character.sprite.bind_texture(uniform["texture enabled"]);
character.sprite.draw(uniform["mvp"], view, projection);
character.draw(curve(), uniform["mvp"], view, projection, uniform["texture enabled"]);
/* Update FPS indicator display to the current FPS count and draw. */
if (configuration()["display"]["fps"])

View File

@ -1,6 +1,29 @@
#include "Character.hpp"
void Character::reset(const Curve& curve)
Character::Character(const Configuration& configuration) : configuration(configuration)
{
/* Initialize sprite and box to the size of the graphic. */
glm::vec2 size {20.0f / 486.0f};
_sprite = Sprite("resource/cake-frames/cake1.png", size);
_box.size(size);
}
void Character::load()
{
_sprite.load();
}
const Box& Character::box() const
{
return _box;
}
const Sprite& Character::sprite() const
{
return _sprite;
}
void Character::beginning(const Curve& curve)
{
next_point_index = 0;
speed = 0.0f;
@ -8,11 +31,6 @@ void Character::reset(const Curve& curve)
accelerating = false;
}
const nlohmann::json& Character::entry(const std::string& suffix) const
{
return configuration["character"][*profile.current() + "-" + suffix];
}
void Character::update(const Curve& curve, const sb::Timer& timer)
{
/* Adjust speed based on acceleration state and character profile. */
@ -74,4 +92,20 @@ void Character::update(const Curve& curve, const sb::Timer& timer)
distance_remaining = 0;
}
}
/* Update collision box location. */
_box.center(position);
}
void Character::draw(const Curve& curve, GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform)
{
glm::vec2 translation = sb::wrap_point(position, {-curve.aspect, -1.0f, 0.0f}, {curve.aspect, 1.0f, 1.0f});
_sprite.translate(glm::vec3{translation, 0.0f});
_sprite.bind_texture(texture_flag_uniform);
_sprite.draw(transformation_uniform, view, projection);
}
const nlohmann::json& Character::entry(const std::string& suffix) const
{
return configuration["character"][*profile.current() + "-" + suffix];
}

View File

@ -15,9 +15,11 @@ private:
inline static const std::vector<std::string> PROFILES = {"cake", "ball", "buffalo"};
Configuration& configuration;
const Configuration& configuration;
float speed = 0.0f;
int next_point_index = 0;
Sprite _sprite;
Box _box;
/*!
* @param suffix the character profile config value to get, without the character name
@ -30,14 +32,28 @@ public:
sb::Switch<> accelerating = false;
sb::Selection<std::vector<std::string>> profile = PROFILES;
glm::vec3 position;
Sprite sprite;
Character(Configuration& configuration) : configuration(configuration) {}
Character(const Configuration& configuration);
/*!
* Load the graphics into the GPU. This is only necessary to run if the object was constructed before the GL context was loaded.
*/
void load();
/*!
* @return reference to the box object enclosing the character in world space
*/
const Box& box() const;
/*!
* @return reference to the character's sprite object for accessing the character's graphics
*/
const Sprite& sprite() const;
/*!
* @param curve the curve to reset the beginning position to
*/
void reset(const Curve& curve);
void beginning(const Curve& curve);
/*!
* Check acceleration state and adjust speed. Move character toward the next point on the curve.
@ -46,4 +62,15 @@ public:
* @param timer a timer object that is updated once per frame, so that it provides delta time for movement
*/
void update(const Curve& curve, const sb::Timer& timer);
/*!
* Perform GL drawing operations using the character's sprite object.
*
* @param curve curve which determines how to wrap the character's position to the curve's space
* @param transformation_uniform transformation uniform location in the shader program
* @param view view transformation matrix
* @param projection projection transformation matrix
* @param texture_flag_uniform uniform location in the shader program of the boolean that turns texture drawing on or off
*/
void draw(const Curve& curve, GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform);
};

View File

@ -1,16 +1,33 @@
#include "Enemy.hpp"
bool Enemy::collide(const sb::Box& box, const Sprite& sprite, const glm::vec3& clip_lower, const glm::vec3& clip_upper) const
{
sb::Box wrapped = this->box;
wrapped.center(sb::wrap_point({this->box.center(), 0.0f}, clip_lower, clip_upper));
sb::Box wrapped_other = box;
wrapped_other.center(sb::wrap_point({box.center(), 0.0f}, clip_lower, clip_upper));
return wrapped.collide(wrapped_other);
}
Slicer::Slicer(const Curve& curve, float relative, float speed, float stray) : curve(curve), relative(relative), speed(speed), stray(stray)
{
reset();
// reset();
position = start();
sprite.texture("resource/slicer/slicer-1.png");
sprite.scale(glm::vec2{12.0f / 486.0f});
/* Get size as the pixels divided by the original resolution */
float size = 12.0f / 486.0f;
/* Apply size to sprite and box objects */
sprite.scale(glm::vec2{size});
box.size(2.0f * glm::vec2{size});
};
void Slicer::reset()
{
position = start();
sprite.translate(curve.get().wrap(position));
// position = start();
// sprite.translate(curve.get().wrap(position));
// box.center(position);
}
void Slicer::update(const sb::Timer& timer)
@ -33,6 +50,7 @@ void Slicer::update(const sb::Timer& timer)
toward_end = !toward_end;
}
position += glm::vec3{sb::Segment(position, destination).step(move), 0.0f};
box.center(position);
sprite.translate(curve.get().wrap(position));
}
@ -68,13 +86,17 @@ glm::vec3 Slicer::end() const
Fish::Fish(const Curve& curve, float relative, float speed, float radius) : curve(curve), relative(relative), speed(speed), radius(radius)
{
sprite.texture("resource/fish/fish-1.png");
sprite.scale(glm::vec2{12.0f / 486.0f});
reset();
/* Set size of objects */
glm::vec2 size {12.0f / 486.0f};
sprite.scale(size);
box.size(2.0f * size);
// reset();
};
void Fish::reset()
{
angle = 0.0f;
// angle = 0.0f;
}
void Fish::update(const sb::Timer& timer)
@ -82,6 +104,7 @@ void Fish::update(const sb::Timer& timer)
angle += timer.delta(speed);
glm::vec3 position = center() + glm::vec3{sb::angle_to_vector(angle, radius), 0.0f};
sprite.translate(curve.get().wrap(position));
box.center(position);
}
void Fish::draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform)
@ -102,13 +125,22 @@ Projectile::Projectile(const glm::vec3& position, const glm::vec3& target, float
/* Initialize sprite */
sprite.texture("resource/projectile/projectile-1.png");
sprite.scale(glm::vec2{12.0f / 486.0f});
/* Set size of objects */
glm::vec2 size {12.0f / 486.0f};
sprite.scale(size);
box.size(2.0f * size);
/* Place objects at initial position */
sprite.translate(position);
box.center(position);
};
void Projectile::update(const sb::Timer& timer)
{
position += glm::vec3{sb::angle_to_vector(angle, timer.delta(speed)), 0.0f};
sprite.translate(position);
box.center(position);
}
void Projectile::draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform)
@ -127,8 +159,15 @@ Projector::Projector(const Character& character, const glm::vec3& position, floa
{
animation_charge.frame_length(rate);
sprite.texture("resource/projector/projector-1.png");
sprite.scale(glm::vec2{12.0f / 486.0f});
/* Set size and position of objects */
glm::vec2 size {12.0f / 486.0f};
sprite.scale(size);
sprite.translate(position);
box.size(2.0f * size);
box.center(position);
/* Charge animation will always be playing */
animation_charge.play();
};
@ -172,6 +211,18 @@ void Projector::draw(GLuint transformation_uniform, const glm::mat4 view, const
}
}
bool Projector::collide(const sb::Box& box, const Sprite& sprite, const glm::vec3& clip_lower, const glm::vec3& clip_upper) const
{
for (const Projectile& projectile : projectiles)
{
if (projectile.collide(box, sprite, clip_lower, clip_upper))
{
return true;
}
}
return false;
}
void Projector::charge()
{
animation_release.play_once(release_delay);
@ -184,10 +235,13 @@ void Projector::release()
Flame::Flame(const glm::vec3& position, float speed, float angle, float mirror_interval) : position(position), speed(speed), angle(angle)
{
/* Initialize sprite */
/* Initialize object size and position */
glm::vec2 size {12.0f / 486.0f};
sprite.texture("resource/flame/flame-1.png");
sprite.scale(glm::vec2{12.0f / 486.0f});
sprite.scale(size);
sprite.translate(position);
box.size(2.0f * size);
box.center(position);
/* Only start the mirror effect if the interval is a positive value. */
if (mirror_interval >= 0.0f)
@ -202,6 +256,7 @@ void Flame::update(const sb::Timer& timer)
animation_mirror.update(timer.stamp());
position += glm::vec3{sb::angle_to_vector(angle, timer.delta(speed)), 0.0f};
sprite.translate(position);
box.center(position);
}
void Flame::draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform)

View File

@ -3,6 +3,7 @@
#include <functional>
#include "glm/glm.hpp"
#include "Timer.hpp"
#include "Box.hpp"
#include "Animation.hpp"
#include "Curve.hpp"
#include "Sprite.hpp"
@ -13,6 +14,8 @@ class Enemy
protected:
sb::Box box;
/*!
* Default destructor, defined as virtual so that the auto generated derived destructors will be called.
*/
@ -26,9 +29,33 @@ public:
virtual void update(const sb::Timer& timer) = 0;
/*!
* Perform GL drawing operations using the given uniform locations and transformation matrices.
*
* @param transformation_uniform transformation uniform location in the shader program
* @param view view transformation matrix
* @param projection projection transformation matrix
* @param texture_flag_uniform uniform location in the shader program of the boolean that turns texture drawing on or off
*/
virtual void draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform) = 0;
/*!
* Check whether the enemy collides with the given ::Sprite positioned at the given ::Box. The sprite object is used to perform
* pixel collision, so alpha pixels that collide will not be counted as a collision.
*
* The enemy and character are tracked without wrapping their coordinates, so the clip for wrapping must be given to this function
* in order to determine if they collide.
*
* Note: per-pixel collision not implemented yet.
*
* @param box position in world coordinates to check for a collision with the enemy's own box object
* @param sprite graphics to use for per pixel collision
* @param clip_lower clip area lower bounds to wrap the position to
* @param clip_upper clip area upper bounds to wrap the position to
* @return true if the sprite object's pixels enclosed in the box object collides with the pixels enclosed by
* this enemy's box object
*/
virtual bool collide(const sb::Box& box, const Sprite& sprite, const glm::vec3& clip_lower, const glm::vec3& clip_upper) const;
};
/*!
@ -69,6 +96,7 @@ public:
Slicer(const Curve& curve, float relative, float speed = 0.51440329f, float stray = 0.24691358f);
/*!
* Place at Slicer::start() position, updating collision box and sprite objects.
*/
void reset();
@ -127,7 +155,8 @@ public:
};
/*!
* Create an enemy which is a projectile that has been fired toward a location by a ::Projector object.
* Create an enemy which is a projectile that has been fired toward a location by a ::Projector object. It continues travelling in
* the same direction even after reaching the point, and goes off screen.
*/
class Projectile : public Enemy
{
@ -217,11 +246,18 @@ public:
*/
void draw(GLuint transformation_uniform, const glm::mat4 view, const glm::mat4 projection, GLuint texture_flag_uniform);
/*!
* Check if projectiles collide.
*
* @see Enemy::collide(const sb::Box&, const Sprite&, const glm::vec3&, const glm::vec3&)
*/
bool collide(const sb::Box& box, const Sprite& sprite, const glm::vec3& clip_lower, const glm::vec3& clip_upper) const;
};
/*!
* Create an enemy which is a flame that constantly scrolls with a given angle direction and speed from its initial position, optionally
* mirroring to scroll the opposite direction at a given interval.
* mirroring to scroll the opposite direction at a given interval. The enemy wraps at the edges of the screen, so it is always on screen.
*/
class Flame : public Enemy
{

View File

@ -3,7 +3,7 @@
#include "glm/glm.hpp"
#include "Model.hpp"
class Sprite : private sb::Plane
class Sprite : public sb::Plane
{
private:
@ -93,7 +93,7 @@ public:
* Add a new texture to ::Sprite from a path to an image file which will converted into a new texture.
*
* The texture is loaded into GPU memory if the GL context is active. Otherwise, the path is just attached to
* the texture, and it must be loaded with a call to ::load or Texture::load.
* the texture, and it must be loaded with a call to Model::load or Texture::load.
*
* @param path path to an image
*/
@ -177,17 +177,6 @@ public:
Model::transform(_translation * _scale * _rotation * transformation);
}
/*!
* @return box object that represents the world coordinates of the rectangle surrounding the sprite object
*/
sb::Box box() const;
/*!
* @param sprite another sprite to check against for a collision
* @return true if the two sprite objects collide, false otherwise
*/
bool collide(const Sprite& sprite) const;
/*
*/
void bind_texture() const