Work
This commit is contained in:
parent
6edc140a19
commit
0d841135a5
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
35
src/engine/object.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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 |
Loading…
x
Reference in New Issue
Block a user