This commit is contained in:
Thomas Forgione 2022-08-06 14:14:00 +02:00
parent 6edc140a19
commit 0d841135a5
7 changed files with 206 additions and 75 deletions

View File

@ -28,4 +28,12 @@ impl Bbox {
size,
}
}
/// Returns true is the two bboxes overlap.
pub fn overlap(self, rhs: Bbox) -> bool {
rhs.position.x < self.position.x + self.size.x
&& rhs.position.x + rhs.size.x > self.position.x
&& rhs.position.y < self.position.y + self.size.y
&& rhs.position.y + rhs.size.y > self.position.y
}
}

View File

@ -94,6 +94,32 @@ impl CollisionAxis {
_ => true,
}
}
/// Adds another collision axis.
pub fn and(self, other: CollisionAxis) -> CollisionAxis {
match (self, other) {
(CollisionAxis::X, CollisionAxis::X) => CollisionAxis::X,
(CollisionAxis::X, CollisionAxis::Y) => CollisionAxis::Both,
(CollisionAxis::Y, CollisionAxis::X) => CollisionAxis::Both,
(CollisionAxis::Y, CollisionAxis::Y) => CollisionAxis::Y,
(CollisionAxis::Both, _) => CollisionAxis::Both,
(_, CollisionAxis::Both) => CollisionAxis::Both,
}
}
/// Adds another collision axis to options.
pub fn and_option(first: Option<CollisionAxis>, other: CollisionAxis) -> Option<CollisionAxis> {
Some(match (first, other) {
(None, CollisionAxis::X) => CollisionAxis::X,
(None, CollisionAxis::Y) => CollisionAxis::Y,
(Some(CollisionAxis::X), CollisionAxis::X) => CollisionAxis::X,
(Some(CollisionAxis::X), CollisionAxis::Y) => CollisionAxis::Both,
(Some(CollisionAxis::Y), CollisionAxis::X) => CollisionAxis::Both,
(Some(CollisionAxis::Y), CollisionAxis::Y) => CollisionAxis::Y,
(Some(CollisionAxis::Both), _) => CollisionAxis::Both,
(_, CollisionAxis::Both) => CollisionAxis::Both,
})
}
}
/// This struct represents the different sides from which a collision can occur.
@ -146,7 +172,7 @@ impl CollisionTile {
/// This struct represents a renderable tile linking to its part in the tileset texture.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct GraphicTile(Option<i32>);
pub struct GraphicTile(pub Option<i32>);
impl GraphicTile {
/// Creates the correct graphic tile depending on the neighbours.
@ -230,6 +256,82 @@ pub struct PositionedTile {
pub position: Vector,
}
impl PositionedTile {
/// Returns the collision information between a collision tile and a moving object.
pub fn collides(self, old: Bbox, new: Bbox) -> Option<(CollisionAxis, Vector)> {
let mut collision_x = false;
let mut collision_y = false;
let mut new = new;
let tile = Bbox::new(self.position.x, self.position.y, SPRITE_SIZE, SPRITE_SIZE);
loop {
// I know, this loop is ugly, but I don't care
if !new.overlap(tile) {
break;
}
// Collisions between feet and ground
if self.collision.from_top
&& old.position.y + old.size.y <= tile.position.y
&& new.position.y + new.size.y >= tile.position.y
{
collision_y = true;
new.position.y = tile.position.y - new.size.y;
}
if !new.overlap(tile) {
break;
}
// Collisions between right and right wall
if self.collision.from_left
&& old.position.x + old.size.x <= tile.position.x
&& new.position.x + new.size.x >= tile.position.x
{
collision_x = true;
new.position.x = tile.position.x - new.size.x;
}
if !new.overlap(tile) {
break;
}
// Collisions between left and left wall
if self.collision.from_right
&& old.position.x >= tile.position.x + SPRITE_SIZE
&& new.position.x <= tile.position.x + SPRITE_SIZE
{
collision_x = true;
new.position.x = tile.position.x + SPRITE_SIZE;
}
if !new.overlap(tile) {
break;
}
// Collisions between head and roof
if self.collision.from_bottom
&& old.position.y >= tile.position.y + SPRITE_SIZE
&& new.position.y <= tile.position.y + SPRITE_SIZE
{
collision_y = true;
new.position.y = tile.position.y + SPRITE_SIZE;
}
break;
}
match (collision_x, collision_y) {
(true, true) => Some((CollisionAxis::Both, new.position)),
(true, false) => Some((CollisionAxis::X, new.position)),
(false, true) => Some((CollisionAxis::Y, new.position)),
(false, false) => None,
}
}
}
impl Drawable for PositionedTile {
fn texture(&self) -> Texture {
Texture::Overworld
@ -472,67 +574,15 @@ impl Map {
rows as f64,
) as usize;
let mut collision_x = false;
let mut collision_y = false;
let mut collision_axis: Option<CollisionAxis> = None;
let mut new = new;
for col in min_col..=max_col {
for row in min_row..=max_row {
let tile_left = col as f64 * SPRITE_SIZE;
let tile_top = row as f64 * SPRITE_SIZE;
let tile = Bbox::new(tile_left, tile_top, SPRITE_SIZE, SPRITE_SIZE);
if !overlap(new, tile) {
continue;
}
// Collisions between feet and ground
if self.collision_tiles[(row, col)].from_top
&& old.position.y + old.size.y <= tile_top
&& new.position.y + new.size.y >= tile_top
{
collision_y = true;
new.position.y = tile_top - new.size.y;
}
if !overlap(new, tile) {
continue;
}
// Collisions between right and right wall
if self.collision_tiles[(row, col)].from_left
&& old.position.x + old.size.x <= tile_left
&& new.position.x + new.size.x >= tile_left
{
collision_x = true;
new.position.x = tile_left - new.size.x;
}
if !overlap(new, tile) {
continue;
}
// Collisions between left and left wall
if self.collision_tiles[(row, col)].from_right
&& old.position.x >= tile_left + SPRITE_SIZE
&& new.position.x <= tile_left + SPRITE_SIZE
{
collision_x = true;
new.position.x = tile_left + SPRITE_SIZE;
}
if !overlap(new, tile) {
continue;
}
// Collisions between head and roof
if self.collision_tiles[(row, col)].from_bottom
&& old.position.y >= tile_top + SPRITE_SIZE
&& new.position.y <= tile_top + SPRITE_SIZE
{
collision_y = true;
new.position.y = tile_top + SPRITE_SIZE;
if let Some((axis, new_pos)) = self.at(row, col).collides(old, new) {
new.position = new_pos;
collision_axis = CollisionAxis::and_option(collision_axis, axis);
}
}
}
@ -540,35 +590,25 @@ impl Map {
// Collision between the player and left border of the level
if new.position.x < 0.0 {
new.position.x = 0.0;
collision_x = true;
collision_axis = CollisionAxis::and_option(collision_axis, CollisionAxis::X);
}
// Collision between the player and right border of the level
if new.position.x > cols as f64 * SPRITE_SIZE {
new.position.x = cols as f64 * SPRITE_SIZE;
collision_x = true;
collision_axis = CollisionAxis::and_option(collision_axis, CollisionAxis::Y);
}
// Collision between the player and the void
if new.position.y > self.collision_tiles.rows() as f64 * SPRITE_SIZE {
new.position.y = self.collision_tiles.rows() as f64 * SPRITE_SIZE;
collision_y = true;
death = true;
}
match (collision_x, collision_y) {
(true, true) => Some((CollisionAxis::Both, new.position, death)),
(true, false) => Some((CollisionAxis::X, new.position, death)),
(false, true) => Some((CollisionAxis::Y, new.position, death)),
(false, false) => None,
if let Some(collision_axis) = collision_axis {
Some((collision_axis, new.position, death))
} else {
None
}
}
}
/// Checks if two boxes overlap.
pub fn overlap(box1: Bbox, box2: Bbox) -> bool {
box2.position.x < box1.position.x + box1.size.x
&& box2.position.x + box2.size.x > box1.position.x
&& box2.position.y < box1.position.y + box1.size.y
&& box2.position.y + box2.size.y > box1.position.y
}

View File

@ -6,6 +6,7 @@ pub mod controls;
pub mod input;
pub mod map;
pub mod math;
pub mod object;
pub mod physics;
pub mod scene;
pub mod texture;
@ -268,6 +269,11 @@ impl Engine {
}
}
// Draw the objects
for o in self.scene.objects() {
self.draw(o, view, window_size)?;
}
// Draw characters
for c in self.scene.characters() {
if true {
@ -284,6 +290,10 @@ impl Engine {
let source = drawable.texture_rect(self.after_loop);
let mut dest = source.clone();
// Without view
// dest.position = drawable.position();
// With view
dest.position = drawable.position() - view.position;
dest.position.x *= window.x / view.size.x;
dest.position.y *= window.y / view.size.y;

35
src/engine/object.rs Normal file
View File

@ -0,0 +1,35 @@
//! This module contains objects that are not characters.
use std::time::SystemTime;
use crate::engine::bbox::Bbox;
use crate::engine::texture::{Texture, SPRITE_SIZE};
use crate::engine::vector::Vector;
use crate::engine::Drawable;
/// An object that can collide with the character.
pub struct Object {
/// The initial position of the object.
pub position: Vector,
}
impl Object {
/// Creates a new object.
pub fn new(position: Vector) -> Object {
Object { position }
}
}
impl Drawable for Object {
fn texture(&self) -> Texture {
Texture::Overworld
}
fn texture_rect(&self, _now: SystemTime) -> Bbox {
Bbox::new(46.0 * SPRITE_SIZE, 0.0, SPRITE_SIZE, SPRITE_SIZE)
}
fn position(&self) -> Vector {
self.position
}
}

View File

@ -6,14 +6,19 @@ use crate::engine::bbox::Bbox;
use crate::engine::character::Character;
use crate::engine::input::Action;
use crate::engine::input::InputManager;
use crate::engine::map::Map;
use crate::engine::map::{CollisionTile, GraphicTile, Map, PositionedTile};
use crate::engine::object::Object;
use crate::engine::texture::SPRITE_SIZE;
use crate::engine::vector::Vector;
/// Contains everything needed to play.
pub struct Scene {
/// The characters contained in the scene.
characters: Vec<Character>,
/// The objects contained in the scene.
objects: Vec<Object>,
/// The map of the scene.
map: Map,
}
@ -31,8 +36,10 @@ pub enum State {
impl Scene {
/// Creates a scene from a map level.
pub fn from_map(map: Map) -> Scene {
let object = Object::new(Vector::new(100.0, 300.0));
Scene {
characters: vec![],
objects: vec![object],
map,
}
}
@ -46,6 +53,11 @@ impl Scene {
self.characters.push(character);
}
/// Adds an object to the scene.
pub fn add_object(&mut self, object: Object) {
self.objects.push(object);
}
/// Returns the controlable.
pub fn controlable(&self) -> Option<&Character> {
for character in &self.characters {
@ -115,6 +127,7 @@ impl Scene {
if axis.is_x() {
c.speed.x = 0.0;
}
if axis.is_y() {
c.speed.y = 0.0;
c.ground_collision();
@ -127,6 +140,26 @@ impl Scene {
} else {
c.fall_off();
}
for object in &self.objects {
let positioned = PositionedTile {
graphic: GraphicTile(None),
collision: CollisionTile::full(),
position: object.position,
};
if let Some((axis, position)) = positioned.collides(old, c.bbox()) {
c.position = position - offset;
if axis.is_x() {
c.speed.x = 0.0;
}
if axis.is_y() {
c.speed.y = 0.0;
c.ground_collision();
}
}
}
}
}
@ -149,6 +182,11 @@ impl Scene {
pub fn map(&self) -> &Map {
&self.map
}
/// Returns a reference to the objects of the scene.
pub fn objects(&self) -> &Vec<Object> {
&self.objects
}
}
/// Trait that needs to be implemented for everything that can be updatable.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB