mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-06 15:15:48 -06:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f5af2cd96 | ||
|
|
36a2f00d8c |
2
Justfile
2
Justfile
@@ -41,4 +41,4 @@ samply:
|
|||||||
|
|
||||||
# Build the project for Emscripten
|
# Build the project for Emscripten
|
||||||
web:
|
web:
|
||||||
bun run web.build.ts
|
bun run web.build.ts; caddy file-server --root dist
|
||||||
|
|||||||
16
src/game.rs
16
src/game.rs
@@ -13,6 +13,7 @@ use crate::systems::blinking::Blinking;
|
|||||||
use crate::systems::components::{GhostAnimation, GhostState, LastAnimationState};
|
use crate::systems::components::{GhostAnimation, GhostState, LastAnimationState};
|
||||||
use crate::systems::movement::{BufferedDirection, Position, Velocity};
|
use crate::systems::movement::{BufferedDirection, Position, Velocity};
|
||||||
use crate::systems::profiling::SystemId;
|
use crate::systems::profiling::SystemId;
|
||||||
|
use crate::systems::render::touch_ui_render_system;
|
||||||
use crate::systems::render::RenderDirty;
|
use crate::systems::render::RenderDirty;
|
||||||
use crate::systems::{
|
use crate::systems::{
|
||||||
self, combined_render_system, ghost_collision_system, present_system, Hidden, LinearAnimation, MovementModifiers, NodeId,
|
self, combined_render_system, ghost_collision_system, present_system, Hidden, LinearAnimation, MovementModifiers, NodeId,
|
||||||
@@ -108,9 +109,9 @@ impl Game {
|
|||||||
EventType::ControllerTouchpadDown,
|
EventType::ControllerTouchpadDown,
|
||||||
EventType::ControllerTouchpadMotion,
|
EventType::ControllerTouchpadMotion,
|
||||||
EventType::ControllerTouchpadUp,
|
EventType::ControllerTouchpadUp,
|
||||||
EventType::FingerDown,
|
// EventType::FingerDown, // Enable for touch controls
|
||||||
EventType::FingerUp,
|
// EventType::FingerUp, // Enable for touch controls
|
||||||
EventType::FingerMotion,
|
// EventType::FingerMotion, // Enable for touch controls
|
||||||
EventType::DollarGesture,
|
EventType::DollarGesture,
|
||||||
EventType::DollarRecord,
|
EventType::DollarRecord,
|
||||||
EventType::MultiGesture,
|
EventType::MultiGesture,
|
||||||
@@ -130,9 +131,8 @@ impl Game {
|
|||||||
EventType::Window,
|
EventType::Window,
|
||||||
EventType::MouseWheel,
|
EventType::MouseWheel,
|
||||||
// EventType::MouseMotion,
|
// EventType::MouseMotion,
|
||||||
EventType::MouseButtonDown,
|
// EventType::MouseButtonDown, // Enable for desktop touch testing
|
||||||
EventType::MouseButtonUp,
|
// EventType::MouseButtonUp, // Enable for desktop touch testing
|
||||||
EventType::MouseButtonDown,
|
|
||||||
EventType::AppDidEnterBackground,
|
EventType::AppDidEnterBackground,
|
||||||
EventType::AppWillEnterForeground,
|
EventType::AppWillEnterForeground,
|
||||||
EventType::AppWillEnterBackground,
|
EventType::AppWillEnterBackground,
|
||||||
@@ -159,7 +159,7 @@ impl Game {
|
|||||||
// Create debug texture at output resolution for crisp debug rendering
|
// Create debug texture at output resolution for crisp debug rendering
|
||||||
let output_size = canvas.output_size().unwrap();
|
let output_size = canvas.output_size().unwrap();
|
||||||
let mut debug_texture = texture_creator
|
let mut debug_texture = texture_creator
|
||||||
.create_texture_target(None, output_size.0, output_size.1)
|
.create_texture_target(Some(sdl2::pixels::PixelFormatEnum::ARGB8888), output_size.0, output_size.1)
|
||||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
||||||
|
|
||||||
// Debug texture is copied over the backbuffer, it requires transparency abilities
|
// Debug texture is copied over the backbuffer, it requires transparency abilities
|
||||||
@@ -317,6 +317,7 @@ impl Game {
|
|||||||
world.insert_resource(DebugState::default());
|
world.insert_resource(DebugState::default());
|
||||||
world.insert_resource(AudioState::default());
|
world.insert_resource(AudioState::default());
|
||||||
world.insert_resource(CursorPosition::default());
|
world.insert_resource(CursorPosition::default());
|
||||||
|
world.insert_resource(systems::input::TouchState::default());
|
||||||
world.insert_resource(StartupSequence::new(
|
world.insert_resource(StartupSequence::new(
|
||||||
constants::startup::STARTUP_FRAMES,
|
constants::startup::STARTUP_FRAMES,
|
||||||
constants::startup::STARTUP_TICKS_PER_FRAME,
|
constants::startup::STARTUP_TICKS_PER_FRAME,
|
||||||
@@ -388,6 +389,7 @@ impl Game {
|
|||||||
dirty_render_system,
|
dirty_render_system,
|
||||||
combined_render_system,
|
combined_render_system,
|
||||||
hud_render_system,
|
hud_render_system,
|
||||||
|
touch_ui_render_system,
|
||||||
present_system,
|
present_system,
|
||||||
)
|
)
|
||||||
.chain(),
|
.chain(),
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ pub fn requires_console() -> bool {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn get_canvas_size() -> Option<(u32, u32)> {
|
pub fn get_canvas_size() -> Option<(u32, u32)> {
|
||||||
let mut width = 0.0;
|
let mut width = 0.0;
|
||||||
let mut height = 0.0;
|
let mut height = 0.0;
|
||||||
|
|||||||
@@ -25,6 +25,59 @@ pub enum CursorPosition {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Default, Debug)]
|
||||||
|
pub struct TouchState {
|
||||||
|
pub active_touch: Option<TouchData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TouchData {
|
||||||
|
pub finger_id: i64,
|
||||||
|
pub start_pos: Vec2,
|
||||||
|
pub current_pos: Vec2,
|
||||||
|
pub current_direction: Option<Direction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TouchData {
|
||||||
|
pub fn new(finger_id: i64, start_pos: Vec2) -> Self {
|
||||||
|
Self {
|
||||||
|
finger_id,
|
||||||
|
start_pos,
|
||||||
|
current_pos: start_pos,
|
||||||
|
current_direction: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_position(&mut self, new_pos: Vec2) -> Option<Direction> {
|
||||||
|
self.current_pos = new_pos;
|
||||||
|
let delta = new_pos - self.start_pos;
|
||||||
|
|
||||||
|
// Minimum threshold for direction detection (in pixels)
|
||||||
|
const THRESHOLD: f32 = 20.0;
|
||||||
|
|
||||||
|
if delta.length() < THRESHOLD {
|
||||||
|
self.current_direction = None;
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine primary direction based on larger component
|
||||||
|
let direction = if delta.x.abs() > delta.y.abs() {
|
||||||
|
if delta.x > 0.0 {
|
||||||
|
Direction::Right
|
||||||
|
} else {
|
||||||
|
Direction::Left
|
||||||
|
}
|
||||||
|
} else if delta.y > 0.0 {
|
||||||
|
Direction::Down
|
||||||
|
} else {
|
||||||
|
Direction::Up
|
||||||
|
};
|
||||||
|
|
||||||
|
self.current_direction = Some(direction);
|
||||||
|
Some(direction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Resource, Debug, Clone)]
|
#[derive(Resource, Debug, Clone)]
|
||||||
pub struct Bindings {
|
pub struct Bindings {
|
||||||
key_bindings: HashMap<Keycode, GameCommand>,
|
key_bindings: HashMap<Keycode, GameCommand>,
|
||||||
@@ -131,6 +184,7 @@ pub fn input_system(
|
|||||||
mut writer: EventWriter<GameEvent>,
|
mut writer: EventWriter<GameEvent>,
|
||||||
mut pump: NonSendMut<EventPump>,
|
mut pump: NonSendMut<EventPump>,
|
||||||
mut cursor: ResMut<CursorPosition>,
|
mut cursor: ResMut<CursorPosition>,
|
||||||
|
mut touch_state: ResMut<TouchState>,
|
||||||
) {
|
) {
|
||||||
let mut cursor_seen = false;
|
let mut cursor_seen = false;
|
||||||
// Collect all events for this frame.
|
// Collect all events for this frame.
|
||||||
@@ -159,6 +213,47 @@ pub fn input_system(
|
|||||||
remaining_time: 0.20,
|
remaining_time: 0.20,
|
||||||
};
|
};
|
||||||
cursor_seen = true;
|
cursor_seen = true;
|
||||||
|
|
||||||
|
// Handle mouse motion as touch motion for desktop testing
|
||||||
|
if let Some(ref mut touch_data) = touch_state.active_touch {
|
||||||
|
if let Some(direction) = touch_data.update_position(Vec2::new(x as f32, y as f32)) {
|
||||||
|
writer.write(GameEvent::Command(GameCommand::MovePlayer(direction)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle mouse events as touch for desktop testing
|
||||||
|
Event::MouseButtonDown { x, y, .. } => {
|
||||||
|
let pos = Vec2::new(x as f32, y as f32);
|
||||||
|
touch_state.active_touch = Some(TouchData::new(0, pos)); // Use ID 0 for mouse
|
||||||
|
}
|
||||||
|
Event::MouseButtonUp { .. } => {
|
||||||
|
touch_state.active_touch = None;
|
||||||
|
}
|
||||||
|
// Handle actual touch events for mobile
|
||||||
|
Event::FingerDown { finger_id, x, y, .. } => {
|
||||||
|
// Convert normalized coordinates (0.0-1.0) to screen coordinates
|
||||||
|
let screen_x = x * crate::constants::CANVAS_SIZE.x as f32;
|
||||||
|
let screen_y = y * crate::constants::CANVAS_SIZE.y as f32;
|
||||||
|
let pos = Vec2::new(screen_x, screen_y);
|
||||||
|
touch_state.active_touch = Some(TouchData::new(finger_id, pos));
|
||||||
|
}
|
||||||
|
Event::FingerMotion { finger_id, x, y, .. } => {
|
||||||
|
if let Some(ref mut touch_data) = touch_state.active_touch {
|
||||||
|
if touch_data.finger_id == finger_id {
|
||||||
|
let screen_x = x * crate::constants::CANVAS_SIZE.x as f32;
|
||||||
|
let screen_y = y * crate::constants::CANVAS_SIZE.y as f32;
|
||||||
|
if let Some(direction) = touch_data.update_position(Vec2::new(screen_x, screen_y)) {
|
||||||
|
writer.write(GameEvent::Command(GameCommand::MovePlayer(direction)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::FingerUp { finger_id, .. } => {
|
||||||
|
if let Some(ref touch_data) = touch_state.active_touch {
|
||||||
|
if touch_data.finger_id == finger_id {
|
||||||
|
touch_state.active_touch = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Event::KeyDown { keycode, repeat, .. } => {
|
Event::KeyDown { keycode, repeat, .. } => {
|
||||||
if let Some(key) = keycode {
|
if let Some(key) = keycode {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::constants::CANVAS_SIZE;
|
use crate::constants::CANVAS_SIZE;
|
||||||
use crate::error::{GameError, TextureError};
|
use crate::error::{GameError, TextureError};
|
||||||
use crate::map::builder::Map;
|
use crate::map::builder::Map;
|
||||||
|
use crate::systems::input::TouchState;
|
||||||
use crate::systems::{
|
use crate::systems::{
|
||||||
debug_render_system, BatchedLinesResource, Collider, CursorPosition, DebugState, DebugTextureResource, DeltaTime,
|
debug_render_system, BatchedLinesResource, Collider, CursorPosition, DebugState, DebugTextureResource, DeltaTime,
|
||||||
DirectionalAnimation, LinearAnimation, Position, Renderable, ScoreResource, StartupSequence, SystemId, SystemTimings,
|
DirectionalAnimation, LinearAnimation, Position, Renderable, ScoreResource, StartupSequence, SystemId, SystemTimings,
|
||||||
@@ -114,6 +115,79 @@ pub struct MapTextureResource(pub Texture);
|
|||||||
/// A non-send resource for the backbuffer texture. This just wraps the texture with a type so it can be differentiated when exposed as a resource.
|
/// A non-send resource for the backbuffer texture. This just wraps the texture with a type so it can be differentiated when exposed as a resource.
|
||||||
pub struct BackbufferResource(pub Texture);
|
pub struct BackbufferResource(pub Texture);
|
||||||
|
|
||||||
|
/// Renders touch UI overlay for mobile/testing.
|
||||||
|
pub fn touch_ui_render_system(
|
||||||
|
mut backbuffer: NonSendMut<BackbufferResource>,
|
||||||
|
mut canvas: NonSendMut<&mut Canvas<Window>>,
|
||||||
|
touch_state: Res<TouchState>,
|
||||||
|
mut errors: EventWriter<GameError>,
|
||||||
|
) {
|
||||||
|
if let Some(ref touch_data) = touch_state.active_touch {
|
||||||
|
let _ = canvas.with_texture_canvas(&mut backbuffer.0, |canvas| {
|
||||||
|
// Set blend mode for transparency
|
||||||
|
canvas.set_blend_mode(BlendMode::Blend);
|
||||||
|
|
||||||
|
// Draw semi-transparent circle at touch start position
|
||||||
|
canvas.set_draw_color(Color::RGBA(255, 255, 255, 100));
|
||||||
|
let center = Point::new(touch_data.start_pos.x as i32, touch_data.start_pos.y as i32);
|
||||||
|
|
||||||
|
// Draw a simple circle by drawing filled rectangles (basic approach)
|
||||||
|
let radius = 30;
|
||||||
|
for dy in -radius..=radius {
|
||||||
|
for dx in -radius..=radius {
|
||||||
|
if dx * dx + dy * dy <= radius * radius {
|
||||||
|
let point = Point::new(center.x + dx, center.y + dy);
|
||||||
|
if let Err(e) = canvas.draw_point(point) {
|
||||||
|
errors.write(TextureError::RenderFailed(format!("Touch UI render error: {}", e)).into());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw direction indicator if we have a direction
|
||||||
|
if let Some(direction) = touch_data.current_direction {
|
||||||
|
canvas.set_draw_color(Color::RGBA(0, 255, 0, 150));
|
||||||
|
|
||||||
|
// Draw arrow indicating direction
|
||||||
|
let arrow_length = 40;
|
||||||
|
let (dx, dy) = match direction {
|
||||||
|
crate::map::direction::Direction::Up => (0, -arrow_length),
|
||||||
|
crate::map::direction::Direction::Down => (0, arrow_length),
|
||||||
|
crate::map::direction::Direction::Left => (-arrow_length, 0),
|
||||||
|
crate::map::direction::Direction::Right => (arrow_length, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
let end_point = Point::new(center.x + dx, center.y + dy);
|
||||||
|
if let Err(e) = canvas.draw_line(center, end_point) {
|
||||||
|
errors.write(TextureError::RenderFailed(format!("Touch arrow render error: {}", e)).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw arrowhead (simple approach)
|
||||||
|
let arrow_size = 8;
|
||||||
|
match direction {
|
||||||
|
crate::map::direction::Direction::Up => {
|
||||||
|
let _ = canvas.draw_line(end_point, Point::new(end_point.x - arrow_size, end_point.y + arrow_size));
|
||||||
|
let _ = canvas.draw_line(end_point, Point::new(end_point.x + arrow_size, end_point.y + arrow_size));
|
||||||
|
}
|
||||||
|
crate::map::direction::Direction::Down => {
|
||||||
|
let _ = canvas.draw_line(end_point, Point::new(end_point.x - arrow_size, end_point.y - arrow_size));
|
||||||
|
let _ = canvas.draw_line(end_point, Point::new(end_point.x + arrow_size, end_point.y - arrow_size));
|
||||||
|
}
|
||||||
|
crate::map::direction::Direction::Left => {
|
||||||
|
let _ = canvas.draw_line(end_point, Point::new(end_point.x + arrow_size, end_point.y - arrow_size));
|
||||||
|
let _ = canvas.draw_line(end_point, Point::new(end_point.x + arrow_size, end_point.y + arrow_size));
|
||||||
|
}
|
||||||
|
crate::map::direction::Direction::Right => {
|
||||||
|
let _ = canvas.draw_line(end_point, Point::new(end_point.x - arrow_size, end_point.y - arrow_size));
|
||||||
|
let _ = canvas.draw_line(end_point, Point::new(end_point.x - arrow_size, end_point.y + arrow_size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Renders the HUD (score, lives, etc.) on top of the game.
|
/// Renders the HUD (score, lives, etc.) on top of the game.
|
||||||
pub fn hud_render_system(
|
pub fn hud_render_system(
|
||||||
mut backbuffer: NonSendMut<BackbufferResource>,
|
mut backbuffer: NonSendMut<BackbufferResource>,
|
||||||
|
|||||||
Reference in New Issue
Block a user