Merge branch 'master' into ewpratten/audio
This commit is contained in:
commit
f0c625bf02
BIN
assets/ewpratten/env_testObject/env_testObject.xcf
Normal file
BIN
assets/ewpratten/env_testObject/env_testObject.xcf
Normal file
Binary file not shown.
23
game/dist/assets/env/env_testObject/env_testObject.json
vendored
Normal file
23
game/dist/assets/env/env_testObject/env_testObject.json
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "env_testObject",
|
||||
"bottom_texture": {
|
||||
"file_path": "assets/env/env_testObject/env_testObjectBottom.png"
|
||||
},
|
||||
"top_texture": {
|
||||
"file_path": "assets/env/env_testObject/env_testObjectTop.png"
|
||||
},
|
||||
"footprint_radius": 128.0,
|
||||
"physics_colliders": [
|
||||
{
|
||||
"position": [
|
||||
-118,
|
||||
-60
|
||||
],
|
||||
"size": [
|
||||
230,
|
||||
127
|
||||
]
|
||||
}
|
||||
],
|
||||
"temperature": 5.0
|
||||
}
|
BIN
game/dist/assets/env/env_testObject/env_testObjectBottom.png
vendored
Normal file
BIN
game/dist/assets/env/env_testObject/env_testObjectBottom.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
game/dist/assets/env/env_testObject/env_testObjectTop.png
vendored
Normal file
BIN
game/dist/assets/env/env_testObject/env_testObjectTop.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
8
game/dist/map_gameMap.objects.json
vendored
Normal file
8
game/dist/map_gameMap.objects.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"type":"env",
|
||||
"name":"env_testObject",
|
||||
"position": [0,0],
|
||||
"rotation_radians": 0.5
|
||||
}
|
||||
]
|
@ -25,5 +25,5 @@ thiserror = "1.0.30"
|
||||
approx = "0.5.1"
|
||||
poll-promise = { version = "0.1.0", features = ["tokio"] }
|
||||
tempfile = "3.3.0"
|
||||
nalgebra = "0.30.1"
|
||||
tiled = { version = "0.10.1", path = "../../third_party/rs-tiled" }
|
||||
nalgebra = { version = "0.30.1", features=["serde-serialize"]}
|
||||
|
@ -1 +1,3 @@
|
||||
pub mod player;
|
||||
pub mod player;
|
||||
pub mod world_object;
|
||||
pub mod world_object_package;
|
64
game/game_logic/src/model/world_object.rs
Normal file
64
game/game_logic/src/model/world_object.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use nalgebra as na;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
asset_manager::{load_json_structure, InternalJsonLoadError},
|
||||
rendering::utilities::anim_texture::AnimatedTexture,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct PossiblyAnimatedTexture {
|
||||
/// Signal if the texture is animated or static
|
||||
pub animated: Option<bool>,
|
||||
/// Relative file path from `dist` to the texture
|
||||
pub file_path: String,
|
||||
}
|
||||
|
||||
/// Defines a collider in object space.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct ObjectCollider {
|
||||
/// Position, relative to the object's center (north east is 1,1 south west is -1,-1)
|
||||
pub position: na::Vector2<f32>,
|
||||
/// Possible sizing
|
||||
pub size: Option<na::Vector2<f32>>,
|
||||
/// Possible radius
|
||||
pub radius: Option<f32>,
|
||||
}
|
||||
|
||||
/// Definition of an object. Only one of these should exist *per object*, and they will be GPU instanced.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct WorldObject {
|
||||
/// Object name. Must match the name of the texture
|
||||
pub name: String,
|
||||
/// The object's bottom texture
|
||||
pub bottom_texture: PossiblyAnimatedTexture,
|
||||
/// The object's top texture
|
||||
pub top_texture: Option<PossiblyAnimatedTexture>,
|
||||
/// colliders describing the object's footprint
|
||||
// pub footprint: Vec<ObjectCollider>,
|
||||
pub footprint_radius: Option<f32>,
|
||||
/// Colliders for physics
|
||||
pub physics_colliders: Vec<ObjectCollider>,
|
||||
/// Temperature
|
||||
pub temperature: Option<f32>,
|
||||
/// Friction
|
||||
pub friction: Option<f32>,
|
||||
}
|
||||
|
||||
/// Used to reference an object in the world definition
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct WorldObjectRef {
|
||||
/// Object type
|
||||
#[serde(rename = "type")]
|
||||
pub kind: String,
|
||||
/// Object name
|
||||
pub name: String,
|
||||
/// Object position (tile-space *not* pixel-space). 1,1 being up and to the right
|
||||
pub position: na::Vector2<f32>,
|
||||
/// Object rotation, positive is clockwise
|
||||
pub rotation_radians: f32,
|
||||
}
|
||||
|
||||
|
111
game/game_logic/src/model/world_object_package.rs
Normal file
111
game/game_logic/src/model/world_object_package.rs
Normal file
@ -0,0 +1,111 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use raylib::{texture::Texture2D, RaylibHandle, RaylibThread};
|
||||
|
||||
use crate::{
|
||||
asset_manager::{load_json_structure, load_texture_from_internal_data},
|
||||
rendering::utilities::anim_texture::AnimatedTexture,
|
||||
};
|
||||
|
||||
use super::world_object::{WorldObject, WorldObjectRef};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum WorldObjectPackageLoadError {
|
||||
#[error(transparent)]
|
||||
JsonError(#[from] crate::asset_manager::InternalJsonLoadError),
|
||||
#[error(transparent)]
|
||||
ResourceError(#[from] crate::asset_manager::ResourceLoadError),
|
||||
}
|
||||
|
||||
/// A simply interface for the madness
|
||||
#[derive(Debug)]
|
||||
pub struct WorldObjectPackage {
|
||||
/// The object definitions
|
||||
pub object_definitions: HashMap<String, WorldObject>,
|
||||
/// The object references
|
||||
pub object_references: Vec<WorldObjectRef>,
|
||||
/// Bottom static textures
|
||||
pub bottom_static_textures: HashMap<String, Texture2D>,
|
||||
/// Top static textures
|
||||
pub top_static_textures: HashMap<String, Texture2D>,
|
||||
/// Bottom animated textures
|
||||
pub bottom_animated_textures: HashMap<String, AnimatedTexture>,
|
||||
/// Top animated textures
|
||||
pub top_animated_textures: HashMap<String, AnimatedTexture>,
|
||||
}
|
||||
|
||||
impl WorldObjectPackage {
|
||||
pub fn load(
|
||||
raylib_handle: &mut RaylibHandle,
|
||||
thread: &RaylibThread,
|
||||
map_objects_file_path: &str,
|
||||
) -> Result<Self, WorldObjectPackageLoadError> {
|
||||
// Attempt to load the object reference list
|
||||
let object_references: Vec<WorldObjectRef> = load_json_structure(map_objects_file_path)?;
|
||||
|
||||
// We also need to load the object definitions
|
||||
let mut object_definitions = HashMap::new();
|
||||
let mut bottom_static_textures = HashMap::new();
|
||||
let mut top_static_textures = HashMap::new();
|
||||
let mut bottom_animated_textures = HashMap::new();
|
||||
let mut top_animated_textures = HashMap::new();
|
||||
for reference in &object_references {
|
||||
// If this is a new object, load it.
|
||||
let object_key = format!("{}:{}", reference.kind, reference.name);
|
||||
if !object_definitions.contains_key(object_key.as_str()) {
|
||||
// Construct the file path from the data we know about the reference
|
||||
let path = format!(
|
||||
"assets/{}/{}/{}.json",
|
||||
reference.kind, reference.name, reference.name
|
||||
);
|
||||
|
||||
// Attempt to load the object definition
|
||||
let object_definition: WorldObject = load_json_structure(&path)?;
|
||||
|
||||
// If this object has a static bottom texture, load it
|
||||
if object_definition.bottom_texture.animated.unwrap_or(false) {
|
||||
panic!("Animated bottom textures are not supported yet")
|
||||
} else {
|
||||
// Load the bottom texture and save it
|
||||
bottom_static_textures.insert(
|
||||
object_key.to_string(),
|
||||
load_texture_from_internal_data(
|
||||
raylib_handle,
|
||||
thread,
|
||||
&object_definition.bottom_texture.file_path,
|
||||
)?,
|
||||
);
|
||||
}
|
||||
|
||||
// If there is a top texture, load it
|
||||
if let Some(top_texture) = &object_definition.top_texture {
|
||||
if top_texture.animated.unwrap_or(false) {
|
||||
panic!("Animated top textures are not supported yet")
|
||||
} else {
|
||||
// Load the top texture and save it
|
||||
top_static_textures.insert(
|
||||
object_key.to_string(),
|
||||
load_texture_from_internal_data(
|
||||
raylib_handle,
|
||||
thread,
|
||||
&top_texture.file_path,
|
||||
)?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Store the object definition
|
||||
object_definitions.insert(object_key.to_string(), object_definition);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
object_definitions,
|
||||
object_references,
|
||||
bottom_static_textures,
|
||||
top_static_textures,
|
||||
bottom_animated_textures,
|
||||
top_animated_textures,
|
||||
})
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ use nalgebra::Vector2;
|
||||
use raylib::{
|
||||
color::Color,
|
||||
math::Rectangle,
|
||||
prelude::{RaylibDraw, RaylibDrawHandle},
|
||||
prelude::{RaylibDraw, RaylibDrawHandle, RaylibMode2D},
|
||||
texture::Texture2D,
|
||||
RaylibHandle, RaylibThread,
|
||||
};
|
||||
@ -127,7 +127,7 @@ impl AnimatedTexture {
|
||||
#[profiling::function]
|
||||
pub fn render_frame_by_index(
|
||||
&self,
|
||||
draw_handle: &mut RaylibDrawHandle,
|
||||
draw_handle: &mut RaylibMode2D<RaylibDrawHandle>,
|
||||
index: usize,
|
||||
position: Vector2<f32>,
|
||||
percent_scale: Option<Vector2<f32>>,
|
||||
@ -186,7 +186,7 @@ impl AnimatedTexture {
|
||||
/// Render the animation based on timestamp
|
||||
pub fn render_automatic(
|
||||
&mut self,
|
||||
draw_handle: &mut RaylibDrawHandle,
|
||||
draw_handle: &mut RaylibMode2D<RaylibDrawHandle>,
|
||||
position: Vector2<f32>,
|
||||
percent_scale: Option<Vector2<f32>>,
|
||||
origin: Option<Vector2<f32>>,
|
||||
@ -216,4 +216,11 @@ impl AnimatedTexture {
|
||||
warn!("We somehow got a frame index of None");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Vector2<f32>{
|
||||
Vector2::new(
|
||||
self.texture.width as f32,
|
||||
self.texture.height as f32,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,14 @@
|
||||
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
||||
|
||||
use crate::asset_manager::{load_texture_from_internal_data, InternalData};
|
||||
use crate::{
|
||||
asset_manager::{load_texture_from_internal_data, InternalData},
|
||||
model::world_object_package::WorldObjectPackage,
|
||||
};
|
||||
use nalgebra as na;
|
||||
use raylib::{
|
||||
camera::Camera2D,
|
||||
color::Color,
|
||||
math::Vector2,
|
||||
math::{Rectangle, Vector2},
|
||||
prelude::{RaylibDraw, RaylibDrawHandle, RaylibMode2D},
|
||||
texture::Texture2D,
|
||||
RaylibHandle, RaylibThread,
|
||||
@ -75,12 +78,14 @@ impl ResourceCache for ProgramDataTileCache {
|
||||
pub struct MapRenderer {
|
||||
map: Map,
|
||||
tile_textures: HashMap<PathBuf, Texture2D>,
|
||||
world_objects: WorldObjectPackage,
|
||||
}
|
||||
|
||||
impl MapRenderer {
|
||||
/// Construct a new MapRenderer.
|
||||
pub fn new(
|
||||
tmx_path: &str,
|
||||
objects_path: &str,
|
||||
raylib: &mut RaylibHandle,
|
||||
raylib_thread: &RaylibThread,
|
||||
) -> Result<Self, MapRenderError> {
|
||||
@ -116,18 +121,41 @@ impl MapRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self { map, tile_textures })
|
||||
// Load the world objects
|
||||
let world_objects = WorldObjectPackage::load(raylib, raylib_thread, objects_path).unwrap();
|
||||
|
||||
Ok(Self {
|
||||
map,
|
||||
tile_textures,
|
||||
world_objects,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn sample_friction_at(&self, position: na::Vector2<f32>) -> f32 {
|
||||
pub fn sample_friction_at(&self, world_position: na::Vector2<f32>) -> f32 {
|
||||
// Convert to a tile position
|
||||
let tile_position = na::Vector2::new(
|
||||
(world_position.x / 128.0).floor() as i32,
|
||||
(world_position.y / 128.0).floor() as i32,
|
||||
);
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn sample_temperature_at(&self, position: na::Vector2<f32>) -> f32 {
|
||||
pub fn sample_temperature_at(&self, world_position: na::Vector2<f32>) -> f32 {
|
||||
// Convert to a tile position
|
||||
let tile_position = na::Vector2::new(
|
||||
(world_position.x / 128.0).floor() as i32,
|
||||
(world_position.y / 128.0).floor() as i32,
|
||||
);
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn render_map(&self, draw_handle: &mut RaylibMode2D<RaylibDrawHandle>, camera: &Camera2D, show_debug_grid:bool) {
|
||||
pub fn render_map(
|
||||
&mut self,
|
||||
draw_handle: &mut RaylibMode2D<RaylibDrawHandle>,
|
||||
camera: &Camera2D,
|
||||
show_debug_grid: bool,
|
||||
player_position: na::Vector2<f32>,
|
||||
) {
|
||||
// Get the window corners in world space
|
||||
let screen_width = draw_handle.get_screen_width();
|
||||
let screen_height = draw_handle.get_screen_height();
|
||||
@ -187,8 +215,138 @@ impl MapRenderer {
|
||||
tile_y * tile_height as i32,
|
||||
Color::WHITE,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check if there is an object at this tile
|
||||
for obj_ref in &self.world_objects.object_references {
|
||||
if obj_ref.position.x == sampler_x as f32
|
||||
&& obj_ref.position.y == sampler_y as f32
|
||||
{
|
||||
// Get access to the actual object definition
|
||||
let object_key =
|
||||
format!("{}:{}", obj_ref.kind, obj_ref.name);
|
||||
let obj_def = self
|
||||
.world_objects
|
||||
.object_definitions
|
||||
.get(&object_key)
|
||||
.unwrap();
|
||||
|
||||
// We need to render the base layer of the object
|
||||
if obj_def.bottom_texture.animated.unwrap_or(false) {
|
||||
let tex = self
|
||||
.world_objects
|
||||
.bottom_animated_textures
|
||||
.get_mut(&object_key)
|
||||
.unwrap();
|
||||
tex.render_automatic(
|
||||
draw_handle,
|
||||
obj_ref.position - (tex.size() / 2.0),
|
||||
None,
|
||||
Some(tex.size() / 2.0),
|
||||
Some(obj_ref.rotation_radians.to_degrees()),
|
||||
None,
|
||||
);
|
||||
} else {
|
||||
let tex = self
|
||||
.world_objects
|
||||
.bottom_static_textures
|
||||
.get_mut(&object_key)
|
||||
.unwrap();
|
||||
let p: Vector2 = obj_ref.position.into();
|
||||
let r1 = Rectangle {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
width: tex.width as f32,
|
||||
height: tex.height as f32,
|
||||
};
|
||||
let r2 = Rectangle {
|
||||
x: p.x,
|
||||
y: p.y,
|
||||
width: tex.width as f32,
|
||||
height: tex.height as f32,
|
||||
};
|
||||
|
||||
draw_handle.draw_texture_pro(
|
||||
&tex,
|
||||
r1,
|
||||
r2,
|
||||
Vector2::new(
|
||||
tex.width as f32 / 2.0,
|
||||
tex.height as f32 / 2.0,
|
||||
),
|
||||
obj_ref.rotation_radians.to_degrees(),
|
||||
Color::WHITE,
|
||||
);
|
||||
}
|
||||
|
||||
// If needed we can render the top layer of the object
|
||||
if let Some(top_texture) = &obj_def.top_texture {
|
||||
// We need to detect if the player is in the footprint of the object
|
||||
let mut tint = Color::WHITE;
|
||||
if let Some(footprint_radius) =
|
||||
obj_def.footprint_radius
|
||||
{
|
||||
let player_dist_to_object =
|
||||
(obj_ref.position - player_position).norm();
|
||||
// debug!(
|
||||
// "Player dist to object: {}",
|
||||
// player_dist_to_object
|
||||
// );
|
||||
if player_dist_to_object <= footprint_radius {
|
||||
tint.a = 128;
|
||||
}
|
||||
}
|
||||
|
||||
if top_texture.animated.unwrap_or(false) {
|
||||
let tex = self
|
||||
.world_objects
|
||||
.top_animated_textures
|
||||
.get_mut(&object_key)
|
||||
.unwrap();
|
||||
tex.render_automatic(
|
||||
draw_handle,
|
||||
obj_ref.position - (tex.size() / 2.0),
|
||||
None,
|
||||
Some(tex.size() / 2.0),
|
||||
Some(obj_ref.rotation_radians.to_degrees()),
|
||||
Some(tint),
|
||||
);
|
||||
} else {
|
||||
let tex = self
|
||||
.world_objects
|
||||
.top_static_textures
|
||||
.get_mut(&object_key)
|
||||
.unwrap();
|
||||
let p: Vector2 = obj_ref.position.into();
|
||||
let r1 = Rectangle {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
width: tex.width as f32,
|
||||
height: tex.height as f32,
|
||||
};
|
||||
let r2 = Rectangle {
|
||||
x: p.x,
|
||||
y: p.y,
|
||||
width: tex.width as f32,
|
||||
height: tex.height as f32,
|
||||
};
|
||||
|
||||
draw_handle.draw_texture_pro(
|
||||
&tex,
|
||||
r1,
|
||||
r2,
|
||||
Vector2::new(
|
||||
tex.width as f32 / 2.0,
|
||||
tex.height as f32 / 2.0,
|
||||
),
|
||||
obj_ref.rotation_radians.to_degrees(),
|
||||
tint,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if show_debug_grid {
|
||||
draw_handle.draw_rectangle_lines(
|
||||
tile_x * tile_width as i32,
|
||||
|
@ -30,7 +30,8 @@ impl PlayableScene {
|
||||
thread: &raylib::RaylibThread,
|
||||
constants: &ProjectConstants,
|
||||
) -> Self {
|
||||
let map_renderer = MapRenderer::new("map_gameMap.tmx", raylib_handle, thread).unwrap();
|
||||
|
||||
let map_renderer = MapRenderer::new("map_gameMap.tmx", "map_gameMap.objects.json", raylib_handle, thread).unwrap();
|
||||
|
||||
// Load the game music
|
||||
let game_soundtrack =
|
||||
@ -104,10 +105,10 @@ impl PlayableScene {
|
||||
let mut ctx2d = draw.begin_mode2D(self.camera);
|
||||
|
||||
// Render the map
|
||||
self.world_map.render_map(&mut ctx2d, &self.camera, true);
|
||||
|
||||
let player_size =
|
||||
(constants.tile_size as f32 * constants.player.start_size * self.player.size) as i32;
|
||||
self.world_map.render_map(&mut ctx2d, &self.camera, true, self.player.position);
|
||||
|
||||
let player_size = (constants.tile_size as f32 * constants.player.start_size * self.player.size) as i32;
|
||||
|
||||
ctx2d.draw_rectangle(
|
||||
self.player.position[0] as i32 - player_size / 2,
|
||||
|
@ -24,14 +24,20 @@ impl TestFoxScene {
|
||||
let fox = AnimatedTexture::new(raylib_handle, thread, "chr", "testFox").unwrap();
|
||||
|
||||
// Load the map
|
||||
let map_renderer = MapRenderer::new("map_gameMap.tmx", raylib_handle, thread).unwrap();
|
||||
let map_renderer = MapRenderer::new(
|
||||
"map_gameMap.tmx",
|
||||
"map_gameMap.objects.json",
|
||||
raylib_handle,
|
||||
thread,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Create a camera
|
||||
let camera = Camera2D {
|
||||
target: Vector2 { x: 0.0, y: 0.0 },
|
||||
offset: Vector2 {
|
||||
x: raylib_handle.get_screen_width() as f32,
|
||||
y: (raylib_handle.get_screen_height() as f32) * -0.5,
|
||||
x: raylib_handle.get_screen_width() as f32 * 0.5,
|
||||
y: (raylib_handle.get_screen_height() as f32) * 0.5,
|
||||
},
|
||||
rotation: 0.0,
|
||||
zoom: 1.0,
|
||||
@ -58,15 +64,15 @@ impl TestFoxScene {
|
||||
// Clear the screen
|
||||
draw.clear_background(Color::WHITE);
|
||||
|
||||
// Render the fox
|
||||
self.fox_animation.render_automatic(
|
||||
&mut draw,
|
||||
na::Vector2::new(0.0, 0.0),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
// // Render the fox
|
||||
// self.fox_animation.render_automatic(
|
||||
// &mut draw,
|
||||
// na::Vector2::new(0.0, 0.0),
|
||||
// None,
|
||||
// None,
|
||||
// None,
|
||||
// None,
|
||||
// );
|
||||
|
||||
// Allow the camera to be moved with wasd
|
||||
if draw.is_key_down(KeyboardKey::KEY_W) {
|
||||
@ -87,7 +93,19 @@ impl TestFoxScene {
|
||||
let mut ctx2d = draw.begin_mode2D(self.camera);
|
||||
|
||||
// Render the map
|
||||
self.world_map.render_map(&mut ctx2d, &self.camera, true);
|
||||
self.world_map.render_map(
|
||||
&mut ctx2d,
|
||||
&self.camera,
|
||||
true,
|
||||
na::Vector2::new(self.camera.target.x, self.camera.target.y).into(),
|
||||
);
|
||||
}
|
||||
|
||||
draw.draw_circle(
|
||||
draw.get_screen_width() / 2,
|
||||
draw.get_screen_height() / 2,
|
||||
4.0,
|
||||
Color::RED,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user