Commit 47c5972a authored by Florian Blasius's avatar Florian Blasius 🤘
Browse files

Initial commit.

parents
/.vscode/
/target/
**/*.rs.bk
Cargo.lock
language: rust
rust:
- nightly
sudo: required
install:
- sudo add-apt-repository -y ppa:zoogie/sdl2-snapshots
- sudo apt-get update -qq
- sudo apt-get install -qq libsdl2-dev
notifications:
email: false
[package]
name = "orbgame"
version = "0.1.0"
description = "The Orbital 2D Game engine"
authors = ["Florian Blasius <flovanpt at posteo.de>"]
readme = "README.md"
license = "MIT"
keywords = [
"orbital",
"redox",
"game",
"2D",
"engine",
"tiles"
]
[lib]
name = "orbgame"
path = "src/lib.rs"
[dependencies]
orbclient = "0.3.11"
orbtk = "0.2.26"
orbimage = "0.1.15"
serde = "1.0"
serde_derive = "1.0"
toml = "0.4"
chrono = "0.4"
The MIT License (MIT)
Copyright (c) 2015 Florian Blasius
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# orbgame
Orbital 2D Game Engine. Compatible with Redox and SDL2.
[![Travis Build Status](https://travis-ci.org/redox-os/orbfont.svg?branch=master)](https://travis-ci.org/FloVanGH/orbgame)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
extern crate orbgame;
extern crate orbimage;
extern crate orbtk;
use orbtk::{Point, Rect, Window};
use orbimage::Image;
use orbgame::*;
fn main() {
let mut window = Window::new(Rect::new(100, 100, 800, 600), "OrbAdventure");
let level = Level::from_path("res/adventure_level.toml");
let sheet = Image::from_path(&level.sheet());
let player_image = Image::from_path("res/adventure_character.png");
let map = level.map().clone();
let main_scene = Scene::new();
if let Ok(sheet) = sheet {
// todo: use correct width and height. Maybe need a extra animation file .toml
let player = Entity::new(Rect::new(32, 32, 14, 21), 256.0);
if let Ok(player_image) = player_image {
main_scene
.camera(Camera::new(
Rect::new(0, 0, window.width(), window.height()),
Point::new(
map.column_count() as i32 * map.tile_size() as i32 - 640,
map.row_count() as i32 * map.tile_size() as i32 - 576,
),
))
.level(level)
.level_sheet(sheet)
.player(player)
.player_image(player_image);
}
}
window.add(&main_scene);
window.exec();
}
This diff is collapsed.
use std::cell::Cell;
use orbtk::{Point, Rect};
use Entity;
pub struct Camera {
rect: Cell<Rect>,
maximum: Cell<Point>,
speed: Cell<u32>,
}
impl Camera {
pub fn new(rect: Rect, maximum: Point) -> Self {
Camera {
rect: Cell::new(rect),
maximum: Cell::new(maximum),
speed: Cell::new(256),
}
}
pub fn rect(&self) -> &Cell<Rect> {
&self.rect
}
pub fn maximum(&self) -> &Cell<Point> {
&self.maximum
}
pub fn speed(&self) -> &Cell<u32> {
&self.speed
}
pub fn mov(&mut self, delta: f32, dir_x: i32, dir_y: i32) {
let mut rect = self.rect.get();
let speed = self.speed.get();
let maximum = self.maximum.get();
rect.x += (dir_x as f32 * speed as f32 * delta) as i32;
rect.y += (dir_y as f32 * speed as f32 * delta) as i32;
let zero: i32 = 0;
// adjust to respect the render_bounds
rect.x = zero.max(rect.x.min(maximum.x));
rect.y = zero.max(rect.y.min(maximum.y));
self.rect().set(rect);
}
pub fn follow(&mut self, entity: &mut Entity) {
let mut screen_position = entity.screen_position().get();
let entity_rect = entity.rect().get();
let mut rect = self.rect.get();
let maximum = self.maximum.get();
screen_position.x = rect.width as i32 / 2;
screen_position.y = rect.height as i32 / 2;
// make the camera follow the sprite
rect.x = entity_rect.x - rect.width as i32 / 2;
rect.y = entity_rect.y - rect.height as i32 / 2;
let zero: i32 = 0;
// clamp values
rect.x = zero.max(rect.x.min(maximum.x));
rect.y = zero.max(rect.y.min(maximum.y));
// in map corners, the sprite cannot be placed in the center of the screen
// and we have to change its screen coordinates
// left and right sides
if entity_rect.x < rect.width as i32 / 2
|| entity_rect.x > maximum.x + rect.width as i32 / 2
{
let new_x = entity_rect.x - rect.x;
screen_position.x = new_x;
}
// top and bottom sides
if entity_rect.y < rect.height as i32 / 2
|| entity_rect.y > maximum.y + rect.height as i32 / 2
{
let new_y = entity_rect.y - rect.y;
screen_position.y = new_y;
}
entity.screen_position().set(screen_position);
self.rect.set(rect);
}
}
use std::cell::Cell;
use orbtk::{Point, Rect};
use Map;
pub enum Direction {
Left,
Up,
Right,
Down,
}
pub struct Entity {
rect: Cell<Rect>,
speed: f32,
direction: Direction,
animation_step: f32,
total_animation_steps: f32,
animation_speed: f32,
screen_position: Cell<Point>,
}
impl Entity {
pub fn new(rect: Rect, speed: f32) -> Self {
Entity {
rect: Cell::new(rect),
speed,
animation_step: 0.0,
total_animation_steps: 4.0,
animation_speed: 10.0,
direction: Direction::Down,
screen_position: Cell::new(Point::new(rect.x, rect.y)),
}
}
pub fn mov(&mut self, delta: f32, dir_x: f32, dir_y: f32, map: &Map) {
let mut rect = self.rect.get();
if dir_y > 0.0 {
self.direction = Direction::Down;
self.animation_step += self.animation_speed * delta;
} else if dir_y < 0.0 {
self.direction = Direction::Up;
self.animation_step += self.animation_speed * delta;
} else if dir_x > 0.0 {
self.direction = Direction::Right;
self.animation_step += self.animation_speed * delta;
} else if dir_x < 0.0 {
self.direction = Direction::Left;
self.animation_step += self.animation_speed * delta;
}
if self.animation_step > self.total_animation_steps {
self.animation_step = 0.0;
}
rect.x += (dir_x * self.speed * delta) as i32;
self.check_tile_collison(&mut rect, dir_x, 0.0, map);
rect.y += (dir_y * self.speed * delta) as i32;
self.check_tile_collison(&mut rect, 0.0, dir_y, map);
let max_x = map.column_count() * map.tile_size() as usize - rect.width as usize;
let max_y = map.row_count() * map.tile_size() as usize - rect.height as usize;
let zero_x: i32 = 0 + rect.width as i32;
let zero_y: i32 = 0 + rect.height as i32;
// adjust to respect the render_bounds
rect.x = zero_x.max(rect.x.min(max_x as i32));
rect.y = zero_y.max(rect.y.min(max_y as i32));
self.rect.set(rect);
}
fn check_tile_collison(&mut self, rect: &mut Rect, dir_x: f32, dir_y: f32, map: &Map) {
let left = rect.x as f32 + 1.0;
let right = rect.x as f32 + rect.width as f32 - 1.0;
let top = rect.y as f32 + 1.0;
let bottom = rect.y as f32 + rect.height as f32 - 1.0;
// check for collisions on sprite sides
let collision = map.is_tile_blocked(left, top) || map.is_tile_blocked(right, top)
|| map.is_tile_blocked(right, bottom)
|| map.is_tile_blocked(left, bottom);
if !collision {
return;
}
println!("yes");
if dir_y > 0.0 {
rect.y = map.get_y(map.get_row(bottom)) as i32 - rect.height as i32;
} else if dir_y < 0.0 {
rect.y = map.get_y(map.get_row(top) + 1.0) as i32;
} else if dir_x > 0.0 {
rect.x = map.get_x(map.get_column(right)) as i32 - rect.width as i32;
} else if dir_x < 0.0 {
rect.x = map.get_x(map.get_column(left) + 1.0) as i32;
}
}
pub fn rect(&self) -> &Cell<Rect> {
&self.rect
}
pub fn direction(&self) -> &Direction {
&self.direction
}
pub fn animation_step(&self) -> f32 {
self.animation_step
}
pub fn screen_position(&self) -> &Cell<Point> {
&self.screen_position
}
}
use std::sync::Arc;
use std::io::Read;
use std::fs::File;
use toml;
use Map;
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Level {
sheet: String,
map: Map,
}
impl Level {
/// Loads a new level form the given file path.
pub fn from_path(path: &str) -> Arc<Self> {
let level: Level = {
// todo: handel result
let mut file = File::open(path).unwrap();
let mut buf = Vec::new();
file.read_to_end(&mut buf).unwrap();
toml::from_slice(&buf).unwrap()
};
Arc::new(level)
}
pub fn from_data(data: &[u8]) -> Arc<Self> {
let level: Level = {
// todo: handel result
toml::from_slice(data).unwrap()
};
Arc::new(level)
}
pub fn sheet(&self) -> &String {
&self.sheet
}
pub fn map(&self) -> &Map {
&self.map
}
}
impl Default for Level {
fn default() -> Self {
Level {
sheet: String::from(""),
map: Map::new(),
}
}
}
#[macro_use]
extern crate serde_derive;
extern crate toml;
extern crate orbclient;
extern crate orbimage;
extern crate orbtk;
extern crate chrono;
pub use self::camera::*;
pub use self::entity::*;
pub use self::level::*;
pub use self::map::*;
pub use self::scene::*;
mod camera;
mod entity;
mod level;
mod map;
mod scene;
use std::cell::Cell;
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Layer {
tiles: Vec<Cell<i32>>,
}
impl Layer {
pub fn new() -> Self {
Layer { tiles: Vec::new() }
}
}
#[derive(Clone, Serialize, Deserialize, Default, Debug)]
pub struct Map {
layer_count: usize,
row_count: usize,
column_count: usize,
tile_size: u32,
blocked_tiles: Vec<i32>,
layers: Vec<Layer>,
}
impl Map {
pub fn new() -> Map {
Map {
..Default::default()
}
}
pub fn layer_count(&self) -> usize {
self.layer_count
}
pub fn row_count(&self) -> usize {
self.row_count
}
pub fn column_count(&self) -> usize {
self.column_count
}
pub fn tile_size(&self) -> u32 {
self.tile_size
}
pub fn get_tile(&self, layer: usize, row: usize, column: usize) -> i32 {
if let Some(l) = self.layers.get(layer) {
if let Some(t) = l.tiles.get(row * self.column_count + column) {
return t.get();
}
}
-1
}
pub fn get_column(&self, x: f32) -> f32 {
(x / self.tile_size as f32).floor()
}
pub fn get_row(&self, y: f32) -> f32 {
(y / self.tile_size as f32).floor()
}
pub fn get_x(&self, column: f32) -> f32 {
column * self.tile_size as f32
}
pub fn get_y(&self, row: f32) -> f32 {
row * self.tile_size as f32
}
pub fn is_blocked(&self, column: usize, row: usize) -> bool {
for l in &self.layers {
if let Some(t) = l.tiles.get(row * self.column_count + column) {
if self.blocked_tiles.contains(&t.get()) {
return true;
}
}
}
false
}
pub fn set_tile(&self, layer: usize, column: usize, row: usize, tile: i32) {
if let Some(ref l) = self.layers.get(layer) {
if let Some(ref t) = l.tiles.get(row * self.column_count + column) {
t.set(tile);
}
}
}
pub fn is_tile_blocked(&self, x: f32, y: f32) -> bool {
let column = (x / self.tile_size as f32).floor() as usize;
let row = (y / self.tile_size as f32).floor() as usize;
self.is_blocked(column, row)
}
}
use std::cell::{Cell, RefCell};
use std::sync::Arc;
use std::time::{Duration, SystemTime};
use std::{cmp, slice};
use chrono::*;
use orbclient::{Color, Renderer};
use orbtk::{Event, Place, Point, Rect, Widget};
use orbimage::{Image, ImageRoi};
use Camera;
use Level;
use Entity;
use Direction;
use Map;
pub struct Scene {
rect: Cell<Rect>,
camera: RefCell<Option<Camera>>,
level: RefCell<Option<Arc<Level>>>,
level_sheet: RefCell<Option<Image>>,
direction: Cell<Point>,
player: RefCell<Option<Entity>>,
player_image: RefCell<Option<Image>>,
debug: Cell<bool>,
fps_timer: Cell<Option<DateTime<Local>>>,
}
impl Scene {
pub fn new() -> Arc<Self> {
Arc::new(Scene {
rect: Cell::new(Rect::default()),
camera: RefCell::new(None),
level: RefCell::new(None),
level_sheet: RefCell::new(None),
direction: Cell::new(Point::new(0, 0)),
player: RefCell::new(None),
player_image: RefCell::new(None),
debug: Cell::new(true),
fps_timer: Cell::new(None),
})
}
pub fn camera(&self, camera: Camera) -> &Self {
*self.camera.borrow_mut() = Some(camera);
self
}
pub fn player(&self, player: Entity) -> &Self {
*self.player.borrow_mut() = Some(player);
self
}
pub fn player_image(&self, player_image: Image) -> &Self {
*self.player_image.borrow_mut() = Some(player_image);
self
}
pub fn level(&self, level: Arc<Level>) -> &Self {
*self.level.borrow_mut() = Some(level);
self
}
pub fn level_sheet(&self, level_sheet: Image) -> &Self {
*self.level_sheet.borrow_mut() = Some(level_sheet);
self
}
pub fn debug(&self) -> &Cell<bool> {
&self.debug
}
fn update(&self) {
const FPS: u64 = ((1.0 / 60.0) * 1000.0) as u64;
let mut run = true;
// if let Some(timer) = self.fps_timer.get() {
// if (Local::now().timestamp() as u64 * 1000 + Local::now().timestamp_subsec_millis() as u64)
// - timer.timestamp() as u64 * 1000 + timer.timestamp_subsec_millis() as u64
// >= FPS
// {
// run = true;
// self.fps_timer.set(Some(Local::now()))
// }
// } else {
// self.fps_timer.set(Some(Local::now()));
// run = true;
// }
if run {
if let Some(ref mut player) = *self.player.borrow_mut() {
if let Some(ref level) = *self.level.borrow() {
player.mov(
0.017,
self.direction.get().x as f32,
self.direction.get().y as f32,
level.map(),
);