diff options
Diffstat (limited to 'src/crepe/system')
| -rw-r--r-- | src/crepe/system/AnimatorSystem.cpp | 31 | ||||
| -rw-r--r-- | src/crepe/system/AnimatorSystem.h | 3 | ||||
| -rw-r--r-- | src/crepe/system/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/crepe/system/CollisionSystem.cpp | 548 | ||||
| -rw-r--r-- | src/crepe/system/CollisionSystem.h | 298 | ||||
| -rw-r--r-- | src/crepe/system/InputSystem.cpp | 187 | ||||
| -rw-r--r-- | src/crepe/system/InputSystem.h | 85 | ||||
| -rw-r--r-- | src/crepe/system/ParticleSystem.h | 10 | ||||
| -rw-r--r-- | src/crepe/system/PhysicsSystem.cpp | 13 | ||||
| -rw-r--r-- | src/crepe/system/PhysicsSystem.h | 4 | ||||
| -rw-r--r-- | src/crepe/system/RenderSystem.cpp | 42 | ||||
| -rw-r--r-- | src/crepe/system/RenderSystem.h | 28 | ||||
| -rw-r--r-- | src/crepe/system/ScriptSystem.h | 2 | ||||
| -rw-r--r-- | src/crepe/system/System.h | 5 | 
14 files changed, 1198 insertions, 60 deletions
| diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp index 8bb6465..549c35d 100644 --- a/src/crepe/system/AnimatorSystem.cpp +++ b/src/crepe/system/AnimatorSystem.cpp @@ -1,8 +1,8 @@ -#include <cstdint> +  #include "../api/Animator.h" -#include "../facade/SDLContext.h"  #include "../manager/ComponentManager.h" +#include "api/LoopTimer.h"  #include "AnimatorSystem.h" @@ -10,15 +10,30 @@ using namespace crepe;  void AnimatorSystem::update() {  	ComponentManager & mgr = this->mediator.component_manager; - +	LoopTimer & timer = this->mediator.timer;  	RefVector<Animator> animations = mgr.get_components_by_type<Animator>(); -	uint64_t tick = SDLContext::get_instance().get_ticks(); +	double elapsed_time = timer.get_current_time(); +  	for (Animator & a : animations) {  		if (!a.active) continue; -		// (10 frames per second) -		a.curr_row = (tick / 100) % a.row; -		a.spritesheet.mask.x = (a.curr_row * a.spritesheet.mask.w) + a.curr_col; -		a.spritesheet.mask = a.spritesheet.mask; + +		Animator::Data & ctx = a.data; +		float frame_duration = 1.0f / ctx.fps; + +		int last_frame = ctx.row; + +		int cycle_end = (ctx.cycle_end == -1) ? a.max_rows : ctx.cycle_end; +		int total_frames = cycle_end - ctx.cycle_start; + +		int curr_frame = static_cast<int>(elapsed_time / frame_duration) % total_frames; + +		ctx.row = ctx.cycle_start + curr_frame; +		a.spritesheet.mask.x = ctx.row * a.spritesheet.mask.w; +		a.spritesheet.mask.y = (ctx.col * a.spritesheet.mask.h); + +		if (!ctx.looping && curr_frame == ctx.cycle_start && last_frame == total_frames - 1) { +			a.active = false; +		}  	}  } diff --git a/src/crepe/system/AnimatorSystem.h b/src/crepe/system/AnimatorSystem.h index f8179a9..7d3f565 100644 --- a/src/crepe/system/AnimatorSystem.h +++ b/src/crepe/system/AnimatorSystem.h @@ -2,9 +2,6 @@  #include "System.h" -//TODO: -// control if flip works with animation system -  namespace crepe {  /** diff --git a/src/crepe/system/CMakeLists.txt b/src/crepe/system/CMakeLists.txt index f507b90..6b2e099 100644 --- a/src/crepe/system/CMakeLists.txt +++ b/src/crepe/system/CMakeLists.txt @@ -7,6 +7,7 @@ target_sources(crepe PUBLIC  	RenderSystem.cpp  	AudioSystem.cpp  	AnimatorSystem.cpp +	InputSystem.cpp  )  target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -17,4 +18,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	RenderSystem.h  	AudioSystem.h  	AnimatorSystem.h +	InputSystem.h  ) diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp index c74ca1d..44a0431 100644 --- a/src/crepe/system/CollisionSystem.cpp +++ b/src/crepe/system/CollisionSystem.cpp @@ -1,5 +1,551 @@ +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <functional> +#include <optional> +#include <utility> +#include <variant> + +#include "../manager/ComponentManager.h" +#include "../manager/EventManager.h" +#include "api/BoxCollider.h" +#include "api/CircleCollider.h" +#include "api/Event.h" +#include "api/Metadata.h" +#include "api/Rigidbody.h" +#include "api/Transform.h" +#include "api/Vector2.h" + +#include "Collider.h"  #include "CollisionSystem.h" +#include "types.h" +#include "util/OptionalRef.h"  using namespace crepe; -void CollisionSystem::update() {} +void CollisionSystem::update() { +	std::vector<CollisionInternal> all_colliders; +	game_object_id_t id = 0; +	ComponentManager & mgr = this->mediator.component_manager; +	RefVector<Rigidbody> rigidbodies = mgr.get_components_by_type<Rigidbody>(); +	// Collisions can only happen on object with a rigidbody +	for (Rigidbody & rigidbody : rigidbodies) { +		if (!rigidbody.active) continue; +		id = rigidbody.game_object_id; +		Transform & transform = mgr.get_components_by_id<Transform>(id).front().get(); +		// Check if the boxcollider is active and has the same id as the rigidbody. +		RefVector<BoxCollider> boxcolliders = mgr.get_components_by_type<BoxCollider>(); +		for (BoxCollider & boxcollider : boxcolliders) { +			if (boxcollider.game_object_id != id) continue; +			if (!boxcollider.active) continue; +			all_colliders.push_back({.id = id, +									 .collider = collider_variant{boxcollider}, +									 .transform = transform, +									 .rigidbody = rigidbody}); +		} +		// Check if the circlecollider is active and has the same id as the rigidbody. +		RefVector<CircleCollider> circlecolliders +			= mgr.get_components_by_type<CircleCollider>(); +		for (CircleCollider & circlecollider : circlecolliders) { +			if (circlecollider.game_object_id != id) continue; +			if (!circlecollider.active) continue; +			all_colliders.push_back({.id = id, +									 .collider = collider_variant{circlecollider}, +									 .transform = transform, +									 .rigidbody = rigidbody}); +		} +	} + +	// Check between all colliders if there is a collision +	std::vector<std::pair<CollisionInternal, CollisionInternal>> collided +		= this->gather_collisions(all_colliders); + +	// For both objects call the collision handler +	for (auto & collision_pair : collided) { +		this->collision_handler_request(collision_pair.first, collision_pair.second); +		this->collision_handler_request(collision_pair.second, collision_pair.first); +	} +} + +void CollisionSystem::collision_handler_request(CollisionInternal & this_data, +												CollisionInternal & other_data) { + +	CollisionInternalType type +		= this->get_collider_type(this_data.collider, other_data.collider); +	std::pair<vec2, CollisionSystem::Direction> resolution_data +		= this->collision_handler(this_data, other_data, type); +	ComponentManager & mgr = this->mediator.component_manager; +	OptionalRef<Metadata> this_metadata +		= mgr.get_components_by_id<Metadata>(this_data.id).front().get(); +	OptionalRef<Metadata> other_metadata +		= mgr.get_components_by_id<Metadata>(other_data.id).front().get(); +	OptionalRef<Collider> this_collider; +	OptionalRef<Collider> other_collider; +	switch (type) { +		case CollisionInternalType::BOX_BOX: { +			this_collider = std::get<std::reference_wrapper<BoxCollider>>(this_data.collider); +			other_collider +				= std::get<std::reference_wrapper<BoxCollider>>(other_data.collider); +			break; +		} +		case CollisionInternalType::BOX_CIRCLE: { +			this_collider = std::get<std::reference_wrapper<BoxCollider>>(this_data.collider); +			other_collider +				= std::get<std::reference_wrapper<CircleCollider>>(other_data.collider); +			break; +		} +		case CollisionInternalType::CIRCLE_BOX: { +			this_collider +				= std::get<std::reference_wrapper<CircleCollider>>(this_data.collider); +			other_collider +				= std::get<std::reference_wrapper<BoxCollider>>(other_data.collider); +			break; +		} +		case CollisionInternalType::CIRCLE_CIRCLE: { +			this_collider +				= std::get<std::reference_wrapper<CircleCollider>>(this_data.collider); +			other_collider +				= std::get<std::reference_wrapper<CircleCollider>>(other_data.collider); +			break; +		} +	} + +	// collision info +	crepe::CollisionSystem::CollisionInfo collision_info{ +		.this_collider = this_collider, +		.this_transform = this_data.transform, +		.this_rigidbody = this_data.rigidbody, +		.this_metadata = this_metadata, +		.other_collider = other_collider, +		.other_transform = other_data.transform, +		.other_rigidbody = other_data.rigidbody, +		.other_metadata = other_metadata, +		.resolution = resolution_data.first, +		.resolution_direction = resolution_data.second, +	}; + +	// Determine if static needs to be called +	this->determine_collision_handler(collision_info); +} + +std::pair<vec2, CollisionSystem::Direction> +CollisionSystem::collision_handler(CollisionInternal & data1, CollisionInternal & data2, +								   CollisionInternalType type) { +	vec2 resolution; +	switch (type) { +		case CollisionInternalType::BOX_BOX: { +			const BoxCollider & collider1 +				= std::get<std::reference_wrapper<BoxCollider>>(data1.collider); +			const BoxCollider & collider2 +				= std::get<std::reference_wrapper<BoxCollider>>(data2.collider); +			vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, +															data1.rigidbody); +			vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, +															data2.rigidbody); +			resolution = this->get_box_box_resolution(collider1, collider2, collider_pos1, +													  collider_pos2); +			break; +		} +		case CollisionInternalType::BOX_CIRCLE: { +			const BoxCollider & collider1 +				= std::get<std::reference_wrapper<BoxCollider>>(data1.collider); +			const CircleCollider & collider2 +				= std::get<std::reference_wrapper<CircleCollider>>(data2.collider); +			vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, +															data1.rigidbody); +			vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, +															data2.rigidbody); +			resolution = -this->get_circle_box_resolution(collider2, collider1, collider_pos2, +														  collider_pos1); +			break; +		} +		case CollisionInternalType::CIRCLE_CIRCLE: { +			const CircleCollider & collider1 +				= std::get<std::reference_wrapper<CircleCollider>>(data1.collider); +			const CircleCollider & collider2 +				= std::get<std::reference_wrapper<CircleCollider>>(data2.collider); +			vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, +															data1.rigidbody); +			vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, +															data2.rigidbody); +			resolution = this->get_circle_circle_resolution(collider1, collider2, +															collider_pos1, collider_pos2); +			break; +		} +		case CollisionInternalType::CIRCLE_BOX: { +			const CircleCollider & collider1 +				= std::get<std::reference_wrapper<CircleCollider>>(data1.collider); +			const BoxCollider & collider2 +				= std::get<std::reference_wrapper<BoxCollider>>(data2.collider); +			vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, +															data1.rigidbody); +			vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, +															data2.rigidbody); +			resolution = this->get_circle_box_resolution(collider1, collider2, collider_pos1, +														 collider_pos2); +			break; +		} +	} + +	Direction resolution_direction = Direction::NONE; +	if (resolution.x != 0 && resolution.y != 0) { +		resolution_direction = Direction::BOTH; +	} else if (resolution.x != 0) { +		resolution_direction = Direction::X_DIRECTION; +		if (data1.rigidbody.data.linear_velocity.y != 0) +			resolution.y = data1.rigidbody.data.linear_velocity.y +						   * (resolution.x / data1.rigidbody.data.linear_velocity.x); +	} else if (resolution.y != 0) { +		resolution_direction = Direction::Y_DIRECTION; +		if (data1.rigidbody.data.linear_velocity.x != 0) +			resolution.x = data1.rigidbody.data.linear_velocity.x +						   * (resolution.y / data1.rigidbody.data.linear_velocity.y); +	} + +	return std::make_pair(resolution, resolution_direction); +} + +vec2 CollisionSystem::get_box_box_resolution(const BoxCollider & box_collider1, +											 const BoxCollider & box_collider2, +											 const vec2 & final_position1, +											 const vec2 & final_position2) const { +	vec2 resolution; // Default resolution vector +	vec2 delta = final_position2 - final_position1; + +	// Compute half-dimensions of the boxes +	float half_width1 = box_collider1.dimensions.x / 2.0; +	float half_height1 = box_collider1.dimensions.y / 2.0; +	float half_width2 = box_collider2.dimensions.x / 2.0; +	float half_height2 = box_collider2.dimensions.y / 2.0; + +	// Calculate overlaps along X and Y axes +	float overlap_x = (half_width1 + half_width2) - std::abs(delta.x); +	float overlap_y = (half_height1 + half_height2) - std::abs(delta.y); + +	// Check if there is a collision should always be true +	if (overlap_x > 0 && overlap_y > 0) { +		// Determine the direction of resolution +		if (overlap_x < overlap_y) { +			// Resolve along the X-axis (smallest overlap) +			resolution.x = (delta.x > 0) ? -overlap_x : overlap_x; +		} else if (overlap_y < overlap_x) { +			// Resolve along the Y-axis (smallest overlap) +			resolution.y = (delta.y > 0) ? -overlap_y : overlap_y; +		} else { +			// Equal overlap, resolve both directions with preference +			resolution.x = (delta.x > 0) ? -overlap_x : overlap_x; +			resolution.y = (delta.y > 0) ? -overlap_y : overlap_y; +		} +	} + +	return resolution; +} + +vec2 CollisionSystem::get_circle_circle_resolution(const CircleCollider & circle_collider1, +												   const CircleCollider & circle_collider2, +												   const vec2 & final_position1, +												   const vec2 & final_position2) const { +	vec2 delta = final_position2 - final_position1; + +	// Compute the distance between the two circle centers +	float distance = std::sqrt(delta.x * delta.x + delta.y * delta.y); + +	// Compute the combined radii of the two circles +	float combined_radius = circle_collider1.radius + circle_collider2.radius; + +	// Compute the penetration depth +	float penetration_depth = combined_radius - distance; + +	// Normalize the delta vector to get the collision direction +	vec2 collision_normal = delta / distance; + +	// Compute the resolution vector +	vec2 resolution = -collision_normal * penetration_depth; + +	return resolution; +} + +vec2 CollisionSystem::get_circle_box_resolution(const CircleCollider & circle_collider, +												const BoxCollider & box_collider, +												const vec2 & circle_position, +												const vec2 & box_position) const { +	vec2 delta = circle_position - box_position; + +	// Compute half-dimensions of the box +	float half_width = box_collider.dimensions.x / 2.0f; +	float half_height = box_collider.dimensions.y / 2.0f; + +	// Clamp circle center to the nearest point on the box +	vec2 closest_point; +	closest_point.x = std::clamp(delta.x, -half_width, half_width); +	closest_point.y = std::clamp(delta.y, -half_height, half_height); + +	// Find the vector from the circle center to the closest point +	vec2 closest_delta = delta - closest_point; + +	// Normalize the delta to get the collision direction +	float distance +		= std::sqrt(closest_delta.x * closest_delta.x + closest_delta.y * closest_delta.y); +	vec2 collision_normal = closest_delta / distance; + +	// Compute penetration depth +	float penetration_depth = circle_collider.radius - distance; + +	// Compute the resolution vector +	vec2 resolution = collision_normal * penetration_depth; + +	return resolution; +} + +void CollisionSystem::determine_collision_handler(CollisionInfo & info) { +	// Check rigidbody type for static +	if (info.this_rigidbody.data.body_type == Rigidbody::BodyType::STATIC) return; +	// If second body is static perform the static collision handler in this system +	if (info.other_rigidbody.data.body_type == Rigidbody::BodyType::STATIC) { +		this->static_collision_handler(info); +	}; +	// Call collision event for user +	CollisionEvent data(info); +	EventManager & emgr = this->mediator.event_manager; +	emgr.trigger_event<CollisionEvent>(data, info.this_collider.game_object_id); +} + +void CollisionSystem::static_collision_handler(CollisionInfo & info) { +	// Move object back using calculate move back value +	info.this_transform.position += info.resolution; + +	// If bounce is enabled mirror velocity +	if (info.this_rigidbody.data.elastisity_coefficient > 0) { +		if (info.resolution_direction == Direction::BOTH) { +			info.this_rigidbody.data.linear_velocity.y +				= -info.this_rigidbody.data.linear_velocity.y +				  * info.this_rigidbody.data.elastisity_coefficient; +			info.this_rigidbody.data.linear_velocity.x +				= -info.this_rigidbody.data.linear_velocity.x +				  * info.this_rigidbody.data.elastisity_coefficient; +		} else if (info.resolution_direction == Direction::Y_DIRECTION) { +			info.this_rigidbody.data.linear_velocity.y +				= -info.this_rigidbody.data.linear_velocity.y +				  * info.this_rigidbody.data.elastisity_coefficient; +		} else if (info.resolution_direction == Direction::X_DIRECTION) { +			info.this_rigidbody.data.linear_velocity.x +				= -info.this_rigidbody.data.linear_velocity.x +				  * info.this_rigidbody.data.elastisity_coefficient; +		} +	} +	// Stop movement if bounce is disabled +	else { +		info.this_rigidbody.data.linear_velocity = {0, 0}; +	} +} + +std::vector<std::pair<CollisionSystem::CollisionInternal, CollisionSystem::CollisionInternal>> +CollisionSystem::gather_collisions(std::vector<CollisionInternal> & colliders) { + +	// TODO: +	// If no colliders skip +	// Check if colliders has rigidbody if not skip + +	// TODO: +	// If amount is higer than lets say 16 for now use quadtree otwerwise skip +	// Quadtree code +	// Quadtree is placed over the input vector + +	// Return data of collided colliders which are variants +	std::vector<std::pair<CollisionInternal, CollisionInternal>> collisions_ret; +	//using visit to visit the variant to access the active and id. +	for (size_t i = 0; i < colliders.size(); ++i) { +		for (size_t j = i + 1; j < colliders.size(); ++j) { +			if (colliders[i].id == colliders[j].id) continue; +			if (!have_common_layer(colliders[i].rigidbody.data.collision_layers, +								   colliders[j].rigidbody.data.collision_layers)) +				continue; +			CollisionInternalType type +				= get_collider_type(colliders[i].collider, colliders[j].collider); +			if (!get_collision( +					{ +						.collider = colliders[i].collider, +						.transform = colliders[i].transform, +						.rigidbody = colliders[i].rigidbody, +					}, +					{ +						.collider = colliders[j].collider, +						.transform = colliders[j].transform, +						.rigidbody = colliders[j].rigidbody, +					}, +					type)) +				continue; +			collisions_ret.emplace_back(colliders[i], colliders[j]); +		} +	} + +	return collisions_ret; +} + +bool CollisionSystem::have_common_layer(const std::set<int> & layers1, +										const std::set<int> & layers2) { + +	// Check if any number is equal in the layers +	for (int num : layers1) { +		if (layers2.contains(num)) { +			// Common layer found +			return true; +			break; +		} +	} +	// No common layer found +	return false; +} + +CollisionSystem::CollisionInternalType +CollisionSystem::get_collider_type(const collider_variant & collider1, +								   const collider_variant & collider2) const { +	if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider1)) { +		if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) { +			return CollisionInternalType::CIRCLE_CIRCLE; +		} else { +			return CollisionInternalType::CIRCLE_BOX; +		} +	} else { +		if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) { +			return CollisionInternalType::BOX_CIRCLE; +		} else { +			return CollisionInternalType::BOX_BOX; +		} +	} +} + +bool CollisionSystem::get_collision(const CollisionInternal & first_info, +									const CollisionInternal & second_info, +									CollisionInternalType type) const { +	switch (type) { +		case CollisionInternalType::BOX_BOX: { +			const BoxCollider & box_collider1 +				= std::get<std::reference_wrapper<BoxCollider>>(first_info.collider); +			const BoxCollider & box_collider2 +				= std::get<std::reference_wrapper<BoxCollider>>(second_info.collider); +			return this->get_box_box_collision(box_collider1, box_collider2, +											   first_info.transform, second_info.transform, +											   second_info.rigidbody, second_info.rigidbody); +		} +		case CollisionInternalType::BOX_CIRCLE: { +			const BoxCollider & box_collider +				= std::get<std::reference_wrapper<BoxCollider>>(first_info.collider); +			const CircleCollider & circle_collider +				= std::get<std::reference_wrapper<CircleCollider>>(second_info.collider); +			return this->get_box_circle_collision( +				box_collider, circle_collider, first_info.transform, second_info.transform, +				second_info.rigidbody, second_info.rigidbody); +		} +		case CollisionInternalType::CIRCLE_CIRCLE: { +			const CircleCollider & circle_collider1 +				= std::get<std::reference_wrapper<CircleCollider>>(first_info.collider); +			const CircleCollider & circle_collider2 +				= std::get<std::reference_wrapper<CircleCollider>>(second_info.collider); +			return this->get_circle_circle_collision( +				circle_collider1, circle_collider2, first_info.transform, +				second_info.transform, second_info.rigidbody, second_info.rigidbody); +		} +		case CollisionInternalType::CIRCLE_BOX: { +			const CircleCollider & circle_collider +				= std::get<std::reference_wrapper<CircleCollider>>(first_info.collider); +			const BoxCollider & box_collider +				= std::get<std::reference_wrapper<BoxCollider>>(second_info.collider); +			return this->get_box_circle_collision( +				box_collider, circle_collider, first_info.transform, second_info.transform, +				second_info.rigidbody, second_info.rigidbody); +		} +	} +	return false; +} + +bool CollisionSystem::get_box_box_collision(const BoxCollider & box1, const BoxCollider & box2, +											const Transform & transform1, +											const Transform & transform2, +											const Rigidbody & rigidbody1, +											const Rigidbody & rigidbody2) const { +	// Get current positions of colliders +	vec2 final_position1 = this->get_current_position(box1.offset, transform1, rigidbody1); +	vec2 final_position2 = this->get_current_position(box2.offset, transform2, rigidbody2); + +	// Calculate half-extents (half width and half height) +	float half_width1 = box1.dimensions.x / 2.0; +	float half_height1 = box1.dimensions.y / 2.0; +	float half_width2 = box2.dimensions.x / 2.0; +	float half_height2 = box2.dimensions.y / 2.0; + +	// Check if the boxes overlap along the X and Y axes +	return (final_position1.x + half_width1 > final_position2.x - half_width2 +			&& final_position1.x - half_width1 < final_position2.x + half_width2 +			&& final_position1.y + half_height1 > final_position2.y - half_height2 +			&& final_position1.y - half_height1 < final_position2.y + half_height2); +} + +bool CollisionSystem::get_box_circle_collision(const BoxCollider & box1, +											   const CircleCollider & circle2, +											   const Transform & transform1, +											   const Transform & transform2, +											   const Rigidbody & rigidbody1, +											   const Rigidbody & rigidbody2) const { +	// Get current positions of colliders +	vec2 final_position1 = this->get_current_position(box1.offset, transform1, rigidbody1); +	vec2 final_position2 = this->get_current_position(circle2.offset, transform2, rigidbody2); + +	// Calculate box half-extents +	float half_width = box1.dimensions.x / 2.0; +	float half_height = box1.dimensions.y / 2.0; + +	// Find the closest point on the box to the circle's center +	float closest_x = std::max(final_position1.x - half_width, +							   std::min(final_position2.x, final_position1.x + half_width)); +	float closest_y = std::max(final_position1.y - half_height, +							   std::min(final_position2.y, final_position1.y + half_height)); + +	// Calculate the distance squared between the circle's center and the closest point on the box +	float distance_x = final_position2.x - closest_x; +	float distance_y = final_position2.y - closest_y; +	float distance_squared = distance_x * distance_x + distance_y * distance_y; + +	// Compare distance squared with the square of the circle's radius +	return distance_squared < circle2.radius * circle2.radius; +} + +bool CollisionSystem::get_circle_circle_collision(const CircleCollider & circle1, +												  const CircleCollider & circle2, +												  const Transform & transform1, +												  const Transform & transform2, +												  const Rigidbody & rigidbody1, +												  const Rigidbody & rigidbody2) const { +	// Get current positions of colliders +	vec2 final_position1 = this->get_current_position(circle1.offset, transform1, rigidbody1); +	vec2 final_position2 = this->get_current_position(circle2.offset, transform2, rigidbody2); + +	float distance_x = final_position1.x - final_position2.x; +	float distance_y = final_position1.y - final_position2.y; +	float distance_squared = distance_x * distance_x + distance_y * distance_y; + +	// Calculate the sum of the radii +	float radius_sum = circle1.radius + circle2.radius; + +	// Check if the distance between the centers is less than or equal to the sum of the radii +	return distance_squared < radius_sum * radius_sum; +} + +vec2 CollisionSystem::get_current_position(const vec2 & collider_offset, +										   const Transform & transform, +										   const Rigidbody & rigidbody) const { +	// Get the rotation in radians +	float radians1 = transform.rotation * (M_PI / 180.0); + +	// Calculate total offset with scale +	vec2 total_offset = (rigidbody.data.offset + collider_offset) * transform.scale; + +	// Rotate +	float rotated_total_offset_x1 +		= total_offset.x * cos(radians1) - total_offset.y * sin(radians1); +	float rotated_total_offset_y1 +		= total_offset.x * sin(radians1) + total_offset.y * cos(radians1); + +	// Final positions considering scaling and rotation +	return (transform.position + vec2(rotated_total_offset_x1, rotated_total_offset_y1)); +} diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h index c1a70d8..5b136c6 100644 --- a/src/crepe/system/CollisionSystem.h +++ b/src/crepe/system/CollisionSystem.h @@ -1,13 +1,311 @@  #pragma once +#include <optional> +#include <variant> +#include <vector> + +#include "api/BoxCollider.h" +#include "api/CircleCollider.h" +#include "api/Event.h" +#include "api/Metadata.h" +#include "api/Rigidbody.h" +#include "api/Transform.h" +#include "api/Vector2.h" + +#include "Collider.h"  #include "System.h"  namespace crepe { +//! A system responsible for detecting and handling collisions between colliders.  class CollisionSystem : public System {  public:  	using System::System; + +private: +	//! A variant type that can hold either a BoxCollider or a CircleCollider. +	using collider_variant = std::variant<std::reference_wrapper<BoxCollider>, +										  std::reference_wrapper<CircleCollider>>; + +	//! Enum representing the types of collider pairs for collision detection. +	enum class CollisionInternalType { +		BOX_BOX, +		CIRCLE_CIRCLE, +		BOX_CIRCLE, +		CIRCLE_BOX, +	}; + +	/** +		* \brief A structure to store the collision data of a single collider. +		* +		* This structure all components and id that are for needed within this system when calculating or handeling collisions. +		* The transform and rigidbody are mostly needed for location and rotation. +		* In rigidbody additional info is written about what the body of the object is, +		* and how it should respond on a collision. +		*/ +	struct CollisionInternal { +		game_object_id_t id = 0; +		collider_variant collider; +		Transform & transform; +		Rigidbody & rigidbody; +	}; + +	//! Enum representing movement directions during collision resolution. +	enum class Direction { +		//! No movement required. +		NONE, +		//! Movement in the X direction. +		X_DIRECTION, +		//! Movement in the Y direction. +		Y_DIRECTION, +		//! Movement in both X and Y directions. +		BOTH +	}; + +public: +	/** +		* \brief Structure representing detailed collision information between two colliders. +		* +		* Includes information about the colliding objects and the resolution data for handling the collision. +		*/ +	struct CollisionInfo { +		Collider & this_collider; +		Transform & this_transform; +		Rigidbody & this_rigidbody; +		Metadata & this_metadata; +		Collider & other_collider; +		Transform & other_transform; +		Rigidbody & other_rigidbody; +		Metadata & other_metadata; +		//! The resolution vector for the collision. +		vec2 resolution; +		//! The direction of movement for resolving the collision. +		Direction resolution_direction = Direction::NONE; +	}; + +public: +	//! Updates the collision system by checking for collisions between colliders and handling them.  	void update() override; + +private: +	/** +		* \brief Determines the type of collider pair from two colliders. +		* +		* Uses std::holds_alternative to identify the types of the provided colliders. +		* +		* \param collider1 First collider variant (BoxCollider or CircleCollider). +		* \param collider2 Second collider variant (BoxCollider or CircleCollider). +		* \return The combined type of the two colliders. +		*/ +	CollisionInternalType get_collider_type(const collider_variant & collider1, +											const collider_variant & collider2) const; + +	/** +		* \brief Calculates the current position of a collider. +		* +		* Combines the Collider offset, Transform position, and Rigidbody offset to compute the position of the collider. +		* +		* \param collider_offset The offset of the collider. +		* \param transform The Transform of the associated game object. +		* \param rigidbody The Rigidbody of the associated game object. +		* \return The calculated position of the collider. +		*/ +	vec2 get_current_position(const vec2 & collider_offset, const Transform & transform, +							  const Rigidbody & rigidbody) const; + +private: +	/** +		* \brief Handles collision resolution between two colliders. +		* +		* Processes collision data and adjusts objects to resolve collisions and/or calls the user oncollision script function. +		* +		* \param data1 Collision data for the first collider. +		* \param data2 Collision data for the second collider. +		*/ +	void collision_handler_request(CollisionInternal & data1, CollisionInternal & data2); + +	/** +		* \brief Resolves collision between two colliders and calculates the movement required. +		* +		* Determines the displacement and direction needed to separate colliders based on their types. +		* +		* \param data1 Collision data for the first collider. +		* \param data2 Collision data for the second collider. +		* \param type The type of collider pair. +		* \return A pair containing the resolution vector and direction for the first collider. +		*/ +	std::pair<vec2, Direction> collision_handler(CollisionInternal & data1, +												 CollisionInternal & data2, +												 CollisionInternalType type); + +	/** +		* \brief Calculates the resolution vector for two BoxColliders. +		* +		* Computes the displacement required to separate two overlapping BoxColliders. +		* +		* \param box_collider1 The first BoxCollider. +		* \param box_collider2 The second BoxCollider. +		* \param position1 The position of the first BoxCollider. +		* \param position2 The position of the second BoxCollider. +		* \return The resolution vector for the collision. +		*/ +	vec2 get_box_box_resolution(const BoxCollider & box_collider1, +								const BoxCollider & box_collider2, const vec2 & position1, +								const vec2 & position2) const; + +	/** +		* \brief Calculates the resolution vector for two CircleCollider. +		* +		* Computes the displacement required to separate two overlapping CircleCollider. +		* +		* \param circle_collider1 The first CircleCollider. +		* \param circle_collider2 The second CircleCollider. +		* \param final_position1 The position of the first CircleCollider. +		* \param final_position2 The position of the second CircleCollider. +		* \return The resolution vector for the collision. +		*/ +	vec2 get_circle_circle_resolution(const CircleCollider & circle_collider1, +									  const CircleCollider & circle_collider2, +									  const vec2 & final_position1, +									  const vec2 & final_position2) const; + +	/** +		* \brief Calculates the resolution vector for two CircleCollider. +		* +		* Computes the displacement required to separate two overlapping CircleCollider. +		* +		* \param circle_collider The first CircleCollider. +		* \param box_collider The second CircleCollider. +		* \param circle_position The position of the CircleCollider. +		* \param box_position The position of the BoxCollider. +		* \return The resolution vector for the collision. +		*/ +	vec2 get_circle_box_resolution(const CircleCollider & circle_collider, +								   const BoxCollider & box_collider, +								   const vec2 & circle_position, +								   const vec2 & box_position) const; + +	/** +		* \brief Determines the appropriate collision handler for a collision. +		* +		* Decides the correct resolution process based on the dynamic or static nature of the colliders involved. +		* +		* \param info Collision information containing data about both colliders. +		*/ +	void determine_collision_handler(CollisionInfo & info); + +	/** +		* \brief Handles collisions involving static objects. +		* +		* Resolves collisions by adjusting positions and modifying velocities if bounce is enabled. +		* +		* \param info Collision information containing data about both colliders. +		*/ +	void static_collision_handler(CollisionInfo & info); + +private: +	/** +		* \brief Checks for collisions between colliders. +		* +		* Identifies collisions and generates pairs of colliding objects for further processing. +		* +		* \param colliders A collection of all active colliders. +		* \return A list of collision pairs with their associated data. +		*/ +	std::vector<std::pair<CollisionInternal, CollisionInternal>> +	gather_collisions(std::vector<CollisionInternal> & colliders); + +	/** +	 * \brief Checks if two collision layers have at least one common layer. +	 * +	 * This function checks if there is any overlapping layer between the two inputs. +	 * It compares each layer from the first input to see +	 * if it exists in the second input. If at least one common layer is found, +	 * the function returns true, indicating that the two colliders share a common +	 * collision layer. +	 * +	 * \param layers1 all collision layers for the first collider. +	 * \param layers2 all collision layers for the second collider. +	 * \return Returns true if there is at least one common layer, false otherwise. +	 */ + +	bool have_common_layer(const std::set<int> & layers1, const std::set<int> & layers2); + +	/** +		* \brief Checks for collision between two colliders. +		* +		* Calls the appropriate collision detection function based on the collider types. +		* +		* \param first_info Collision data for the first collider. +		* \param second_info Collision data for the second collider. +		* \param type The type of collider pair. +		* \return True if a collision is detected, otherwise false. +		*/ +	bool get_collision(const CollisionInternal & first_info, +					   const CollisionInternal & second_info, +					   CollisionInternalType type) const; + +	/** +		* \brief Detects collisions between two BoxColliders. +		* +		* \param box1 The first BoxCollider. +		* \param box2 The second BoxCollider. +		* \param transform1 Transform of the first object. +		* \param transform2 Transform of the second object. +		* \param rigidbody1 Rigidbody of the first object. +		* \param rigidbody2 Rigidbody of the second object. +		* \return True if a collision is detected, otherwise false. +		*/ +	bool get_box_box_collision(const BoxCollider & box1, const BoxCollider & box2, +							   const Transform & transform1, const Transform & transform2, +							   const Rigidbody & rigidbody1, +							   const Rigidbody & rigidbody2) const; + +	/** +	 * \brief Check collision for box on circle collider +	 * +	 * \param box1 The BoxCollider +	 * \param circle2 The CircleCollider +	 * \param transform1 Transform of the first object. +	 * \param transform2 Transform of the second object. +	 * \param rigidbody1 Rigidbody of the first object. +	 * \param rigidbody2 Rigidbody of the second object. +	 * \return True if a collision is detected, otherwise false. +	 */ +	bool get_box_circle_collision(const BoxCollider & box1, const CircleCollider & circle2, +								  const Transform & transform1, const Transform & transform2, +								  const Rigidbody & rigidbody1, +								  const Rigidbody & rigidbody2) const; + +	/** +	 * \brief Check collision for circle on circle collider +	 * +	 * \param circle1 First CircleCollider +	 * \param circle2 Second CircleCollider +	 * \param transform1 Transform of the first object. +	 * \param transform2 Transform of the second object. +	 * \param rigidbody1 Rigidbody of the first object. +	 * \param rigidbody2 Rigidbody of the second object. +	 * \return True if a collision is detected, otherwise false. +	 * +	 * \return status of collision +	 */ +	bool get_circle_circle_collision(const CircleCollider & circle1, +									 const CircleCollider & circle2, +									 const Transform & transform1, +									 const Transform & transform2, +									 const Rigidbody & rigidbody1, +									 const Rigidbody & rigidbody2) const; +}; + +/** + * \brief Event triggered during a collision between objects. + */ +class CollisionEvent : public Event { +public: +	crepe::CollisionSystem::CollisionInfo info; +	CollisionEvent(const crepe::CollisionSystem::CollisionInfo & collisionInfo) +		: info(collisionInfo) {}  };  } // namespace crepe diff --git a/src/crepe/system/InputSystem.cpp b/src/crepe/system/InputSystem.cpp new file mode 100644 index 0000000..aaa8bdf --- /dev/null +++ b/src/crepe/system/InputSystem.cpp @@ -0,0 +1,187 @@ +#include "../api/Button.h" +#include "../manager/ComponentManager.h" +#include "../manager/EventManager.h" + +#include "InputSystem.h" + +using namespace crepe; + +void InputSystem::update() { +	ComponentManager & mgr = this->mediator.component_manager; +	EventManager & event_mgr = this->mediator.event_manager; +	std::vector<SDLContext::EventData> event_list = SDLContext::get_instance().get_events(); +	RefVector<Button> buttons = mgr.get_components_by_type<Button>(); +	RefVector<Camera> cameras = mgr.get_components_by_type<Camera>(); +	OptionalRef<Camera> curr_cam_ref; +	// Find the active camera +	for (Camera & cam : cameras) { +		if (!cam.active) continue; +		curr_cam_ref = cam; +		break; +	} +	if (!curr_cam_ref) return; +	Camera & current_cam = curr_cam_ref; +	RefVector<Transform> transform_vec +		= mgr.get_components_by_id<Transform>(current_cam.game_object_id); +	Transform & cam_transform = transform_vec.front().get(); +	int camera_origin_x = cam_transform.position.x + current_cam.data.postion_offset.x +						  - (current_cam.viewport_size.x / 2); +	int camera_origin_y = cam_transform.position.y + current_cam.data.postion_offset.y +						  - (current_cam.viewport_size.y / 2); + +	for (const SDLContext::EventData & event : event_list) { +		int world_mouse_x = event.mouse_position.x + camera_origin_x; +		int world_mouse_y = event.mouse_position.y + camera_origin_y; +		// check if the mouse is within the viewport +		bool mouse_in_viewport +			= !(world_mouse_x < camera_origin_x +				|| world_mouse_x > camera_origin_x + current_cam.viewport_size.x +				|| world_mouse_y < camera_origin_y +				|| world_mouse_y > camera_origin_y + current_cam.viewport_size.y); + +		switch (event.event_type) { +			case SDLContext::EventType::KEYDOWN: +				event_mgr.queue_event<KeyPressEvent>(KeyPressEvent{ +					.repeat = event.key_repeat, +					.key = event.key, +				}); +				break; +			case SDLContext::EventType::KEYUP: +				event_mgr.queue_event<KeyReleaseEvent>(KeyReleaseEvent{ +					.key = event.key, +				}); +				break; +			case SDLContext::EventType::MOUSEDOWN: +				if (!mouse_in_viewport) { +					break; +				} +				event_mgr.queue_event<MousePressEvent>(MousePressEvent{ +					.mouse_x = world_mouse_x, +					.mouse_y = world_mouse_y, +					.button = event.mouse_button, +				}); +				this->last_mouse_down_position = {world_mouse_x, world_mouse_y}; +				this->last_mouse_button = event.mouse_button; +				break; +			case SDLContext::EventType::MOUSEUP: { +				if (!mouse_in_viewport) { +					break; +				} +				event_mgr.queue_event<MouseReleaseEvent>(MouseReleaseEvent{ +					.mouse_x = world_mouse_x, +					.mouse_y = world_mouse_y, +					.button = event.mouse_button, +				}); +				//check if its a click by checking the last button down +				int delta_x = world_mouse_x - this->last_mouse_down_position.x; +				int delta_y = world_mouse_y - this->last_mouse_down_position.y; + +				if (this->last_mouse_button == event.mouse_button +					&& std::abs(delta_x) <= click_tolerance +					&& std::abs(delta_y) <= click_tolerance) { +					event_mgr.queue_event<MouseClickEvent>(MouseClickEvent{ +						.mouse_x = world_mouse_x, +						.mouse_y = world_mouse_y, +						.button = event.mouse_button, +					}); + +					this->handle_click(event.mouse_button, world_mouse_x, world_mouse_y); +				} +			} break; +			case SDLContext::EventType::MOUSEMOVE: +				if (!mouse_in_viewport) { +					break; +				} +				event_mgr.queue_event<MouseMoveEvent>(MouseMoveEvent{ +					.mouse_x = world_mouse_x, +					.mouse_y = world_mouse_y, +					.delta_x = event.rel_mouse_move.x, +					.delta_y = event.rel_mouse_move.y, +				}); +				this->handle_move(event, world_mouse_x, world_mouse_y); +				break; +			case SDLContext::EventType::MOUSEWHEEL: +				event_mgr.queue_event<MouseScrollEvent>(MouseScrollEvent{ +					.mouse_x = world_mouse_x, +					.mouse_y = world_mouse_y, +					.scroll_direction = event.scroll_direction, +					.scroll_delta = event.scroll_delta, +				}); +				break; +			case SDLContext::EventType::SHUTDOWN: +				event_mgr.queue_event<ShutDownEvent>(ShutDownEvent{}); +				break; +			default: +				break; +		} +	} +} +void InputSystem::handle_move(const SDLContext::EventData & event_data, +							  const int world_mouse_x, const int world_mouse_y) { +	ComponentManager & mgr = this->mediator.component_manager; + +	RefVector<Button> buttons = mgr.get_components_by_type<Button>(); + +	for (Button & button : buttons) { +		RefVector<Transform> transform_vec +			= mgr.get_components_by_id<Transform>(button.game_object_id); +		Transform & transform(transform_vec.front().get()); + +		bool was_hovering = button.hover; +		if (button.active +			&& this->is_mouse_inside_button(world_mouse_x, world_mouse_y, button, transform)) { +			button.hover = true; +			if (!was_hovering && button.on_mouse_enter) { +				button.on_mouse_enter(); +			} +		} else { +			button.hover = false; +			// Trigger the on_exit callback if the hover state just changed to false +			if (was_hovering && button.on_mouse_exit) { +				button.on_mouse_exit(); +			} +		} +	} +} + +void InputSystem::handle_click(const MouseButton & mouse_button, const int world_mouse_x, +							   const int world_mouse_y) { +	ComponentManager & mgr = this->mediator.component_manager; + +	RefVector<Button> buttons = mgr.get_components_by_type<Button>(); + +	for (Button & button : buttons) { +		RefVector<Transform> transform_vec +			= mgr.get_components_by_id<Transform>(button.game_object_id); +		Transform & transform = transform_vec.front().get(); + +		if (button.active +			&& this->is_mouse_inside_button(world_mouse_x, world_mouse_y, button, transform)) { +			this->handle_button_press(button); +		} +	} +} + +bool InputSystem::is_mouse_inside_button(const int mouse_x, const int mouse_y, +										 const Button & button, const Transform & transform) { +	int actual_x = transform.position.x + button.offset.x; +	int actual_y = transform.position.y + button.offset.y; + +	int half_width = button.dimensions.x / 2; +	int half_height = button.dimensions.y / 2; + +	// Check if the mouse is within the button's boundaries +	return mouse_x >= actual_x - half_width && mouse_x <= actual_x + half_width +		   && mouse_y >= actual_y - half_height && mouse_y <= actual_y + half_height; +} + +void InputSystem::handle_button_press(Button & button) { +	if (button.is_toggle) { +		if (!button.is_pressed && button.on_click) { +			button.on_click(); +		} +		button.is_pressed = !button.is_pressed; +	} else if (button.on_click) { +		button.on_click(); +	} +} diff --git a/src/crepe/system/InputSystem.h b/src/crepe/system/InputSystem.h new file mode 100644 index 0000000..87e86f8 --- /dev/null +++ b/src/crepe/system/InputSystem.h @@ -0,0 +1,85 @@ +#pragma once + +#include "../facade/SDLContext.h" +#include "../types.h" +#include "../util/OptionalRef.h" + +#include "System.h" + +namespace crepe { + +class Camera; +class Button; +class Transform; + +/** + * \brief Handles the processing of input events created by SDLContext + * + * This system processes events such as mouse clicks, mouse movement, and keyboard + * actions. It is responsible for detecting interactions with UI buttons and + * passing the corresponding events to the registered listeners. + */ +class InputSystem : public System { +public: +	using System::System; + +	/** +	 * \brief Updates the system, processing all input events. +	 * This method processes all events and triggers corresponding actions. +	 */ +	void update() override; + +private: +	//! Stores the last position of the mouse when the button was pressed. +	ivec2 last_mouse_down_position; +	// TODO: specify world/hud space and make regular `vec2` + +	//! Stores the last mouse button pressed. +	MouseButton last_mouse_button = MouseButton::NONE; + +	//! The maximum allowable distance between mouse down and mouse up to register as a click. +	const int click_tolerance = 5; + +	/** +	* \brief Handles the mouse click event. +	* \param mouse_button The mouse button involved in the click. +	* \param world_mouse_x The X coordinate of the mouse in world space. +	* \param world_mouse_y The Y coordinate of the mouse in world space. +	* +	* This method processes the mouse click event and triggers the corresponding button action. +	*/ +	void handle_click(const MouseButton & mouse_button, const int world_mouse_x, +					  const int world_mouse_y); + +	/** +	* \brief Handles the mouse movement event. +	* \param event_data The event data containing information about the mouse movement. +	* \param world_mouse_x The X coordinate of the mouse in world space. +	* \param world_mouse_y The Y coordinate of the mouse in world space. +	* +	* This method processes the mouse movement event and updates the button hover state. +	*/ +	void handle_move(const SDLContext::EventData & event_data, const int world_mouse_x, +					 const int world_mouse_y); + +	/** +	* \brief Checks if the mouse position is inside the bounds of the button. +	* \param world_mouse_x The X coordinate of the mouse in world space. +	* \param world_mouse_y The Y coordinate of the mouse in world space. +	* \param button The button to check. +	* \param transform The transform component of the button. +	* \return True if the mouse is inside the button, false otherwise. +	*/ +	bool is_mouse_inside_button(const int world_mouse_x, const int world_mouse_y, +								const Button & button, const Transform & transform); + +	/** +	* \brief Handles the button press event, calling the on_click callback if necessary. +	* \param button The button being pressed. +	* +	* This method triggers the on_click action for the button when it is pressed. +	*/ +	void handle_button_press(Button & button); +}; + +} // namespace crepe diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h index c647284..068f01c 100644 --- a/src/crepe/system/ParticleSystem.h +++ b/src/crepe/system/ParticleSystem.h @@ -25,7 +25,7 @@ public:  private:  	/**  	 * \brief Emits a particle from the specified emitter based on its emission properties. -	 *  +	 *  	 * \param emitter Reference to the ParticleEmitter.  	 * \param transform Const reference to the Transform component associated with the emitter.  	 */ @@ -34,7 +34,7 @@ private:  	/**  	 * \brief Calculates the number of times particles should be emitted based on emission rate  	 * and update count. -	 *  +	 *  	 * \param count Current update count.  	 * \param emission Emission rate.  	 * \return The number of particles to emit. @@ -44,7 +44,7 @@ private:  	/**  	 * \brief Checks whether particles are within the emitter’s boundary, resets or stops  	 * particles if they exit. -	 *  +	 *  	 * \param emitter Reference to the ParticleEmitter.  	 * \param transform Const reference to the Transform component associated with the emitter.  	 */ @@ -52,7 +52,7 @@ private:  	/**  	 * \brief Generates a random angle for particle emission within the specified range. -	 *  +	 *  	 * \param min_angle Minimum emission angle in degrees.  	 * \param max_angle Maximum emission angle in degrees.  	 * \return Random angle in degrees. @@ -61,7 +61,7 @@ private:  	/**  	 * \brief Generates a random speed for particle emission within the specified range. -	 *  +	 *  	 * \param min_speed Minimum emission speed.  	 * \param max_speed Maximum emission speed.  	 * \return Random speed. diff --git a/src/crepe/system/PhysicsSystem.cpp b/src/crepe/system/PhysicsSystem.cpp index bebcf3d..ebf4439 100644 --- a/src/crepe/system/PhysicsSystem.cpp +++ b/src/crepe/system/PhysicsSystem.cpp @@ -25,17 +25,20 @@ void PhysicsSystem::update() {  					if (transform.game_object_id == rigidbody.game_object_id) {  						// Add gravity -						if (rigidbody.data.use_gravity) { +						if (rigidbody.data.gravity_scale > 0) {  							rigidbody.data.linear_velocity.y  								+= (rigidbody.data.mass * rigidbody.data.gravity_scale  									* gravity);  						}  						// Add damping -						if (rigidbody.data.angular_damping != 0) { -							rigidbody.data.angular_velocity *= rigidbody.data.angular_damping; +						if (rigidbody.data.angular_velocity_coefficient > 0) { +							rigidbody.data.angular_velocity +								*= rigidbody.data.angular_velocity_coefficient;  						} -						if (rigidbody.data.linear_damping != vec2{0, 0}) { -							rigidbody.data.linear_velocity *= rigidbody.data.linear_damping; +						if (rigidbody.data.linear_velocity_coefficient.x > 0 +							&& rigidbody.data.linear_velocity_coefficient.y > 0) { +							rigidbody.data.linear_velocity +								*= rigidbody.data.linear_velocity_coefficient;  						}  						// Max velocity check diff --git a/src/crepe/system/PhysicsSystem.h b/src/crepe/system/PhysicsSystem.h index 227ab69..26152a5 100644 --- a/src/crepe/system/PhysicsSystem.h +++ b/src/crepe/system/PhysicsSystem.h @@ -6,7 +6,7 @@ namespace crepe {  /**   * \brief System that controls all physics - *  + *   * This class is a physics system that uses a rigidbody and transform to add physics to a game   * object.   */ @@ -15,7 +15,7 @@ public:  	using System::System;  	/**  	 * \brief updates the physics system. -	 *  +	 *  	 * It calculates new velocties and changes the postion in the transform.  	 */  	void update() override; diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp index 0ad685c..26f2c85 100644 --- a/src/crepe/system/RenderSystem.cpp +++ b/src/crepe/system/RenderSystem.cpp @@ -17,13 +17,19 @@  using namespace crepe;  using namespace std; -void RenderSystem::clear_screen() { this->context.clear_screen(); } +void RenderSystem::clear_screen() { +	SDLContext & ctx = this->mediator.sdl_context; +	ctx.clear_screen(); +} -void RenderSystem::present_screen() { this->context.present_screen(); } +void RenderSystem::present_screen() { +	SDLContext & ctx = this->mediator.sdl_context; +	ctx.present_screen(); +} -const Camera & RenderSystem::update_camera() { +SDLContext::CameraValues RenderSystem::update_camera() {  	ComponentManager & mgr = this->mediator.component_manager; - +	SDLContext & ctx = this->mediator.sdl_context;  	RefVector<Camera> cameras = mgr.get_components_by_type<Camera>();  	if (cameras.size() == 0) throw std::runtime_error("No cameras in current scene"); @@ -32,16 +38,18 @@ const Camera & RenderSystem::update_camera() {  		if (!cam.active) continue;  		const Transform & transform  			= mgr.get_components_by_id<Transform>(cam.game_object_id).front().get(); -		this->context.set_camera(cam); -		this->cam_pos = transform.position + cam.offset; -		return cam; +		SDLContext::CameraValues cam_val = ctx.set_camera(cam); +		cam_val.cam_pos = transform.position + cam.data.postion_offset; +		return cam_val;  	}  	throw std::runtime_error("No active cameras in current scene");  }  bool sorting_comparison(const Sprite & a, const Sprite & b) { -	if (a.sorting_in_layer < b.sorting_in_layer) return true; -	if (a.sorting_in_layer == b.sorting_in_layer) return a.order_in_layer < b.order_in_layer; +	if (a.data.sorting_in_layer != b.data.sorting_in_layer) +		return a.data.sorting_in_layer < b.data.sorting_in_layer; +	if (a.data.order_in_layer != b.data.order_in_layer) +		return a.data.order_in_layer < b.data.order_in_layer;  	return false;  } @@ -59,10 +67,11 @@ void RenderSystem::update() {  	this->present_screen();  } -bool RenderSystem::render_particle(const Sprite & sprite, const Camera & cam, +bool RenderSystem::render_particle(const Sprite & sprite, const SDLContext::CameraValues & cam,  								   const double & scale) {  	ComponentManager & mgr = this->mediator.component_manager; +	SDLContext & ctx = this->mediator.sdl_context;  	vector<reference_wrapper<ParticleEmitter>> emitters  		= mgr.get_components_by_id<ParticleEmitter>(sprite.game_object_id); @@ -70,17 +79,16 @@ bool RenderSystem::render_particle(const Sprite & sprite, const Camera & cam,  	bool rendering_particles = false;  	for (const ParticleEmitter & em : emitters) { -		if (!(&em.data.sprite == &sprite)) continue; +		if (&em.data.sprite != &sprite) continue;  		rendering_particles = true;  		if (!em.active) continue;  		for (const Particle & p : em.data.particles) {  			if (!p.active) continue; -			this->context.draw(SDLContext::RenderContext{ +			ctx.draw(SDLContext::RenderContext{  				.sprite = sprite,  				.cam = cam, -				.cam_pos = this->cam_pos,  				.pos = p.position,  				.angle = p.angle,  				.scale = scale, @@ -89,12 +97,12 @@ bool RenderSystem::render_particle(const Sprite & sprite, const Camera & cam,  	}  	return rendering_particles;  } -void RenderSystem::render_normal(const Sprite & sprite, const Camera & cam, +void RenderSystem::render_normal(const Sprite & sprite, const SDLContext::CameraValues & cam,  								 const Transform & tm) { -	this->context.draw(SDLContext::RenderContext{ +	SDLContext & ctx = this->mediator.sdl_context; +	ctx.draw(SDLContext::RenderContext{  		.sprite = sprite,  		.cam = cam, -		.cam_pos = this->cam_pos,  		.pos = tm.position,  		.angle = tm.rotation,  		.scale = tm.scale, @@ -103,7 +111,7 @@ void RenderSystem::render_normal(const Sprite & sprite, const Camera & cam,  void RenderSystem::render() {  	ComponentManager & mgr = this->mediator.component_manager; -	const Camera & cam = this->update_camera(); +	const SDLContext::CameraValues & cam = this->update_camera();  	RefVector<Sprite> sprites = mgr.get_components_by_type<Sprite>();  	RefVector<Sprite> sorted_sprites = this->sort(sprites); diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h index e70831e..e270a6b 100644 --- a/src/crepe/system/RenderSystem.h +++ b/src/crepe/system/RenderSystem.h @@ -18,7 +18,7 @@ class Transform;   * \brief Manages rendering operations for all game objects.   *   * RenderSystem is responsible for rendering, clearing and presenting the screen, and - * managing the active camera.  + * managing the active camera.   */  class RenderSystem : public System {  public: @@ -37,7 +37,7 @@ private:  	void present_screen();  	//! Updates the active camera used for rendering. -	const Camera & update_camera(); +	SDLContext::CameraValues update_camera();  	//! Renders the whole screen  	void render(); @@ -46,39 +46,37 @@ private:  	 * \brief Renders all the particles on the screen from a given sprite.  	 *  	 * \param sprite renders the particles with given texture -	 * \param tm the Transform component for scale +	 * \param tm the Transform component for scale. This is not a const reference because each +	 *  particle has a position and rotation that needs to overwrite the transform position and +	 *  rotation without overwriting the current transform. and because the transform +	 *  constructor is now protected i cannot make tmp inside  	 * \return true if particles have been rendered  	 */ -	bool render_particle(const Sprite & sprite, const Camera & cam, const double & scale); +	bool render_particle(const Sprite & sprite, const SDLContext::CameraValues & cam, +						 const double & scale);  	/** -	 * \brief renders a sprite with a Transform component on the screen  +	 * \brief renders a sprite with a Transform component on the screen  	 *  	 * \param sprite  the sprite component that holds all the data -	 * \param tm the Transform component that holds the position,rotation and scale  +	 * \param tm the Transform component that holds the position,rotation and scale  	 */ -	void render_normal(const Sprite & sprite, const Camera & cam, const Transform & tm); +	void render_normal(const Sprite & sprite, const SDLContext::CameraValues & cam, +					   const Transform & tm);  	/**  	 * \brief sort a vector sprite objects with  	 * -	 * \param objs the vector that will do a sorting algorithm on  +	 * \param objs the vector that will do a sorting algorithm on  	 * \return returns a sorted reference vector  	 */  	RefVector<Sprite> sort(RefVector<Sprite> & objs) const;  	/** -	 * \todo Include color handling for sprites.  	 * \todo Add text rendering using SDL_ttf for text components.  	 * \todo Implement a text component and a button component.  	 * \todo Consider adding text input functionality.  	 */ - -private: -	SDLContext & context = SDLContext::get_instance(); - -	//! camera postion in the current scene -	vec2 cam_pos;  };  } // namespace crepe diff --git a/src/crepe/system/ScriptSystem.h b/src/crepe/system/ScriptSystem.h index 936e9ca..3db1b1e 100644 --- a/src/crepe/system/ScriptSystem.h +++ b/src/crepe/system/ScriptSystem.h @@ -8,7 +8,7 @@ class Script;  /**   * \brief Script system - *  + *   * The script system is responsible for all \c BehaviorScript components, and   * calls the methods on classes derived from \c Script.   */ diff --git a/src/crepe/system/System.h b/src/crepe/system/System.h index 4e7fc6d..063dfbf 100644 --- a/src/crepe/system/System.h +++ b/src/crepe/system/System.h @@ -9,9 +9,8 @@ class ComponentManager;  /**   * \brief Base ECS system class   * - * This class is used as the base for all system classes. Classes derived from - * System must implement the System::update() method and copy Script::Script - * with the `using`-syntax. + * This class is used as the base for all system classes. Classes derived from System must + * implement the System::update() method and copy Script::Script with the `using`-syntax.   */  class System {  public: |