pong/
pong.rs

1use frug::{Color, Event, Instance, Keycode, Vec2d};
2
3struct GameObject {
4    pos: Vec2d<i32>,
5    size: Vec2d<u32>,
6    vel: Vec2d<i32>,
7    color: Color,
8    speed: i32,
9}
10
11impl GameObject {
12    fn get_center(&self) -> Vec2d<i32> {
13        Vec2d {
14            x: self.pos.x + (self.size.x / 2) as i32,
15            y: self.pos.y + (self.size.y / 2) as i32,
16        }
17    }
18
19    fn update(&mut self) {
20        self.pos += self.vel;
21    }
22
23    fn render(&self, instance: &mut Instance) {
24        instance.draw_rect(&self.pos, &self.size, self.color);
25    }
26
27    fn collide_w_object(&mut self, obj: &GameObject) {
28        // Check vertical range
29        if self.pos.y < obj.pos.y + obj.size.y as i32 && self.pos.y + self.size.y as i32 > obj.pos.y
30        {
31            // Check horizontal range
32            if self.pos.x < obj.pos.x + obj.size.x as i32
33                && self.pos.x + self.size.x as i32 > obj.pos.x
34            {
35                self.vel.x *= -1;
36            }
37        }
38    }
39}
40
41impl Default for GameObject {
42    fn default() -> Self {
43        Self {
44            pos: Vec2d { x: 0, y: 0 },
45            size: Vec2d { x: 0, y: 0 },
46            vel: Vec2d { x: 0, y: 0 },
47            color: Color::RGB(255, 255, 255),
48            speed: 4,
49        }
50    }
51}
52
53fn check_reset_ball(ball: &mut GameObject, window_width: i32, window_height: i32) {
54    if ball.pos.x > window_width || (ball.pos.x + ball.size.x as i32) < 0 {
55        ball.pos.x = window_width / 2 - (ball.size.x / 2) as i32;
56        ball.vel.x *= -1;
57        ball.pos.y = window_height / 2 - (ball.size.y / 2) as i32;
58    }
59}
60
61fn main() {
62    const WINDOW_WIDTH: u32 = 800;
63    const WINDOW_HEIGHT: u32 = 600;
64    const TARGET_FPS: u32 = 60;
65    const FRAME_TIME: f32 = 1000.0 / TARGET_FPS as f32;
66
67    let mut frug_instance = Instance::new("Pong", WINDOW_WIDTH, WINDOW_HEIGHT);
68    let background_color = Color::RGB(0, 0, 0);
69
70    // Create game items
71    let mut player = GameObject {
72        pos: Vec2d {
73            x: 50,
74            y: (WINDOW_HEIGHT / 2 - 80 / 2) as i32,
75        },
76        size: Vec2d { x: 10, y: 80 },
77        ..Default::default()
78    };
79    let mut enemy = GameObject {
80        pos: Vec2d {
81            x: (WINDOW_WIDTH - 50 - 10) as i32,
82            y: player.pos.y,
83        },
84        size: player.size.clone(),
85        ..Default::default()
86    };
87    let mut ball = GameObject {
88        pos: Vec2d {
89            x: (WINDOW_WIDTH / 2 - 5) as i32,
90            y: player.pos.y,
91        },
92        size: Vec2d { x: 10, y: 10 },
93        ..Default::default()
94    };
95
96    // Start ball
97    ball.vel.x = ball.speed;
98    ball.vel.y = -ball.speed;
99
100    // Player controls
101    let mut player_up = 0;
102    let mut player_down = 0;
103
104    'running: loop {
105        // Update time/fps control variables
106        let start_time = std::time::Instant::now();
107
108        // Input
109        for event in frug_instance.get_events() {
110            match event {
111                // Quit the application
112                Event::Quit { .. }
113                | Event::KeyDown {
114                    keycode: Some(Keycode::Escape),
115                    ..
116                } => break 'running,
117                Event::KeyDown {
118                    keycode: Some(Keycode::Up),
119                    ..
120                } => {
121                    player_up = 1;
122                }
123                Event::KeyUp {
124                    keycode: Some(Keycode::Up),
125                    ..
126                } => {
127                    player_up = 0;
128                }
129                Event::KeyDown {
130                    keycode: Some(Keycode::Down),
131                    ..
132                } => {
133                    player_down = 1;
134                }
135                Event::KeyUp {
136                    keycode: Some(Keycode::Down),
137                    ..
138                } => {
139                    player_down = 0;
140                }
141                _ => {}
142            }
143        }
144
145        // Control player
146        player.vel.y = (player_down - player_up) * player.speed;
147
148        // Control enemy
149        let ball_center_y = ball.get_center().y;
150        let enemy_center_y = enemy.get_center().y;
151        if ball_center_y < enemy_center_y {
152            enemy.vel.y = -enemy.speed;
153        } else if ball_center_y > enemy_center_y {
154            enemy.vel.y = enemy.speed;
155        }
156
157        // ** Ball collisions **
158        // Vertical borders
159        if ball.pos.y <= 0 || ball.pos.y + ball.size.y as i32 >= WINDOW_HEIGHT as i32 {
160            ball.vel.y *= -1;
161        }
162        // With pallets
163        ball.collide_w_object(&player);
164        ball.collide_w_object(&enemy);
165        // ** Ball collisions **
166
167        // Update
168        player.update();
169        enemy.update();
170        ball.update();
171        check_reset_ball(&mut ball, WINDOW_WIDTH as i32, WINDOW_HEIGHT as i32);
172
173        // Render
174        frug_instance.clear(background_color);
175        player.render(&mut frug_instance);
176        enemy.render(&mut frug_instance);
177        ball.render(&mut frug_instance);
178        frug_instance.present();
179
180        // Control FPS
181        let elapsed_time = start_time.elapsed();
182        let sleep_time = FRAME_TIME - elapsed_time.as_millis() as f32;
183        if sleep_time > 0.0 {
184            std::thread::sleep(std::time::Duration::from_millis(sleep_time as u64));
185        }
186    }
187}