Skip to content

Commit 1530e69

Browse files
Cobrandnrxus
authored andcommitted
Add a simple Game Of Life example
This is mainly meant to show RenderTarget's capabilities and how to use Canvas.
1 parent f872b48 commit 1530e69

File tree

1 file changed

+241
-0
lines changed

1 file changed

+241
-0
lines changed

examples/game-of-life.rs

+241
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
extern crate sdl2;
2+
3+
use sdl2::rect::{Point, Rect};
4+
use sdl2::pixels::Color;
5+
use sdl2::event::Event;
6+
use sdl2::mouse::MouseButton;
7+
use sdl2::keyboard::Keycode;
8+
use sdl2::video::{Window, WindowContext};
9+
use sdl2::render::{Canvas, Texture, TextureCreator};
10+
use game_of_life::{SQUARE_SIZE, PLAYGROUND_WIDTH, PLAYGROUND_HEIGHT};
11+
12+
mod game_of_life {
13+
pub const SQUARE_SIZE: u32 = 16;
14+
pub const PLAYGROUND_WIDTH: u32 = 49;
15+
pub const PLAYGROUND_HEIGHT: u32 = 40;
16+
17+
#[derive(Copy, Clone)]
18+
pub enum State {
19+
Paused,
20+
Playing,
21+
}
22+
23+
pub struct GameOfLife {
24+
playground: [bool; (PLAYGROUND_WIDTH*PLAYGROUND_HEIGHT) as usize],
25+
state: State,
26+
}
27+
28+
impl GameOfLife {
29+
pub fn new() -> GameOfLife {
30+
let mut playground = [false; (PLAYGROUND_WIDTH * PLAYGROUND_HEIGHT) as usize];
31+
32+
// let's make a nice default pattern !
33+
for i in 1..(PLAYGROUND_HEIGHT-1) {
34+
playground[(1 + i* PLAYGROUND_WIDTH) as usize] = true;
35+
playground[((PLAYGROUND_WIDTH-2) + i* PLAYGROUND_WIDTH) as usize] = true;
36+
}
37+
for j in 2..(PLAYGROUND_WIDTH-2) {
38+
playground[(1*PLAYGROUND_WIDTH + j) as usize] = true;
39+
playground[((PLAYGROUND_HEIGHT-2)*PLAYGROUND_WIDTH + j) as usize] = true;
40+
}
41+
42+
GameOfLife {
43+
playground: playground,
44+
state: State::Paused,
45+
}
46+
}
47+
48+
pub fn get(&self, x: i32, y: i32) -> Option<bool> {
49+
if x >= 0 && y >= 0 &&
50+
(x as u32) < PLAYGROUND_WIDTH && (y as u32) < PLAYGROUND_HEIGHT {
51+
Some(self.playground[(x as u32 + (y as u32)* PLAYGROUND_WIDTH) as usize])
52+
} else {
53+
None
54+
}
55+
}
56+
57+
pub fn get_mut<'a>(&'a mut self, x: i32, y: i32) -> Option<&'a mut bool> {
58+
if x >= 0 && y >= 0 &&
59+
(x as u32) < PLAYGROUND_WIDTH && (y as u32) < PLAYGROUND_HEIGHT {
60+
Some(&mut self.playground[(x as u32 + (y as u32)* PLAYGROUND_WIDTH) as usize])
61+
} else {
62+
None
63+
}
64+
}
65+
66+
pub fn toggle_state(&mut self) {
67+
self.state = match self.state {
68+
State::Paused => State::Playing,
69+
State::Playing => State::Paused,
70+
}
71+
}
72+
73+
pub fn state(&self) -> State {
74+
self.state
75+
}
76+
77+
pub fn update(&mut self) {
78+
let mut new_playground = self.playground;
79+
for (u, mut square) in new_playground.iter_mut().enumerate() {
80+
let u = u as u32;
81+
let x = u % PLAYGROUND_WIDTH;
82+
let y = u / PLAYGROUND_WIDTH;
83+
let mut count : u32 = 0;
84+
for i in -1..2 {
85+
for j in -1..2 {
86+
if !(i == 0 && j == 0) {
87+
let peek_x : i32 = (x as i32) + i;
88+
let peek_y : i32 = (y as i32) + j;
89+
match self.get(peek_x, peek_y) {
90+
Some(true) => {
91+
count += 1;
92+
},
93+
_ => {},
94+
}
95+
}
96+
}
97+
}
98+
if count > 3 || count < 2 {
99+
*square = false;
100+
} else if count == 3 {
101+
*square = true;
102+
} else if count == 2 {
103+
*square = *square;
104+
}
105+
}
106+
self.playground = new_playground;
107+
}
108+
}
109+
110+
111+
112+
impl<'a> IntoIterator for &'a GameOfLife {
113+
type Item = &'a bool;
114+
type IntoIter = ::std::slice::Iter<'a, bool>;
115+
fn into_iter(self) -> ::std::slice::Iter<'a, bool> {
116+
self.playground.iter()
117+
}
118+
}
119+
}
120+
121+
fn dummy_texture<'a>(canvas: &mut Canvas<Window>, texture_creator: &'a TextureCreator<WindowContext>) -> Texture<'a>{
122+
let mut square_texture : Texture =
123+
texture_creator.create_texture_target(None, SQUARE_SIZE, SQUARE_SIZE).unwrap();
124+
{
125+
// let's change the texture we just created
126+
let mut texture_canvas = canvas.with_target(&mut square_texture).unwrap();
127+
texture_canvas.set_draw_color(Color::RGB(0, 0, 0));
128+
texture_canvas.clear();
129+
for i in 0..SQUARE_SIZE {
130+
for j in 0..SQUARE_SIZE {
131+
// drawing pixel by pixel isn't very effective, but we only do it once and store
132+
// the texture afterwards so it's still alright!
133+
if (i+j) % 7 == 0 {
134+
// this doesn't mean anything, there was some trial and serror to find
135+
// something that wasn't too ugly
136+
texture_canvas.set_draw_color(Color::RGB(192, 192, 192));
137+
texture_canvas.draw_point(Point::new(i as i32, j as i32)).unwrap();
138+
}
139+
if (i+j*2) % 5 == 0 {
140+
texture_canvas.set_draw_color(Color::RGB(64, 64, 64));
141+
texture_canvas.draw_point(Point::new(i as i32, j as i32)).unwrap();
142+
}
143+
}
144+
}
145+
}
146+
square_texture
147+
}
148+
149+
pub fn main() {
150+
let sdl_context = sdl2::init().unwrap();
151+
let video_subsystem = sdl_context.video().unwrap();
152+
153+
// the window is the representation of a window in your operating system,
154+
// however you can only manipulate properties of that window, like its size, whether it's
155+
// fullscreen, ... but you cannot change its content without using a Canvas or using the
156+
// `surface()` method.
157+
let window = video_subsystem
158+
.window("rust-sdl2 demo: Game of Life",
159+
SQUARE_SIZE*PLAYGROUND_WIDTH,
160+
SQUARE_SIZE*PLAYGROUND_HEIGHT)
161+
.position_centered()
162+
.build()
163+
.unwrap();
164+
165+
// the canvas allows us to both manipulate the property of the window and to change its content
166+
// via hardware or software rendering. See CanvasBuilder for more info.
167+
let mut canvas = window.into_canvas()
168+
.target_texture()
169+
.present_vsync()
170+
.build().unwrap();
171+
172+
println!("Using SDL_Renderer \"{}\"", canvas.info().name);
173+
canvas.set_draw_color(Color::RGB(0, 0, 0));
174+
// clears the canvas with the color we set in `set_draw_color`.
175+
canvas.clear();
176+
// However the canvas has not been updated to the window yet, everything has been processed to
177+
// an internal buffer, but if we want our buffer to be displayed on the window, we need to call
178+
// `present`. We need to call this everytime we want to render a new frame on the window.
179+
canvas.present();
180+
181+
// this struct manages textures. For lifetime reasons, the canvas cannot directly create
182+
// textures, you have to create a `TextureCreator` instead.
183+
let texture_creator : TextureCreator<_> = canvas.texture_creator();
184+
185+
// Create a "target" texture so that we can use our Renderer with it later
186+
let square_texture = dummy_texture(&mut canvas, &texture_creator);
187+
let mut game = game_of_life::GameOfLife::new();
188+
189+
let mut event_pump = sdl_context.event_pump().unwrap();
190+
let mut frame : u32 = 0;
191+
'running: loop {
192+
// get the inputs here
193+
for event in event_pump.poll_iter() {
194+
match event {
195+
Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
196+
break 'running
197+
},
198+
Event::KeyDown { keycode: Some(Keycode::Space), repeat: false, .. } => {
199+
game.toggle_state();
200+
},
201+
Event::MouseButtonDown { x, y, mouse_btn: MouseButton::Left, .. } => {
202+
let x = (x as u32) / SQUARE_SIZE;
203+
let y = (y as u32) / SQUARE_SIZE;
204+
match game.get_mut(x as i32, y as i32) {
205+
Some(mut square) => {*square = !(*square);},
206+
None => {panic!()}
207+
};
208+
},
209+
_ => {}
210+
}
211+
}
212+
213+
// update the game loop here
214+
if frame >= 29 {
215+
game.update();
216+
frame = 0;
217+
}
218+
219+
canvas.set_draw_color(Color::RGB(0, 0, 0));
220+
canvas.clear();
221+
for (i, unit) in (&game).into_iter().enumerate() {
222+
let i = i as u32;
223+
match *unit {
224+
true => {
225+
canvas.copy(&square_texture,
226+
None,
227+
Rect::new(((i % PLAYGROUND_WIDTH) * SQUARE_SIZE) as i32,
228+
((i / PLAYGROUND_WIDTH) * SQUARE_SIZE) as i32,
229+
SQUARE_SIZE,
230+
SQUARE_SIZE)).unwrap();
231+
},
232+
false => {},
233+
}
234+
}
235+
canvas.present();
236+
match game.state() {
237+
game_of_life::State::Playing => { frame += 1; },
238+
_ => {}
239+
};
240+
}
241+
}

0 commit comments

Comments
 (0)