Rust语言俄罗斯方块(漂亮的界面案例+详细的代码解说+完美运行)

tetris-demo A Tetris example written in Rust using Piston in under 500 lines of code

项目地址: https://gitcode.com/gh_mirrors/te/tetris-demo

项目介绍

"Tetris Example in Rust, v2" 是一个用Rust语言编写的俄罗斯方块游戏示例。这个项目不仅是一个简单的游戏实现,更是一个展示Rust编程基础的绝佳范例。通过414行代码,开发者可以深入了解Rust的基本语法和编程思想。此外,项目还提供了一个清晰的Git历史记录,展示了功能的逐步迭代过程,非常适合初学者和有经验的开发者学习参考。

完整代码

use piston_window::{WindowSettings, PistonWindow, Event, RenderEvent, PressEvent};
use piston_window::{Rectangle, DrawState, Context, Graphics};
use piston_window::{Button, Key};

use rand::Rng;

use std::time::{Duration, Instant};
use std::collections::HashMap;

enum DrawEffect<'a> {
    None,
    Darker,
    Flash(&'a Vec<i8>),
}

#[derive(Copy, Clone)]
enum Color {
    Red, Green, Blue, Magenta, Cyan, Yellow, Orange,
}

#[derive(Default, Clone)]
struct Board(HashMap<(i8, i8), Color>);

impl Board {
    fn new(v: &[(i8, i8)], color: Color) -> Self {
        Board(v.iter().cloned().map(|(x, y)| ((x, y), color)).collect())
    }

    fn modified<F>(&self, f: F) -> Self
        where F: Fn((i8, i8)) -> (i8, i8)
    {
        Board(self.0.iter().map(|((x, y), color)| (f((*x, *y)), *color)).collect())
    }

    fn modified_filter<F>(&self, f: F) -> Self
        where F: Fn((i8, i8)) -> Option<(i8, i8)>
    {
        Board(self.0.iter()
            .filter_map(|((x, y), color)| f((*x, *y)).map(|p| (p, *color)))
            .collect())
    }

    fn transposed(&self) -> Self {
        self.modified(|(ox, oy)| (oy, ox))
    }

    fn mirrored_y(&self) -> Self {
        self.modified(|(ox, oy)| (ox, -oy))
    }

    fn rotated(&self) -> Self {
        self.mirrored_y().transposed()
    }

    fn rotated_counter(&self) -> Self {
        self.rotated().rotated().rotated()
    }

    fn negative_shift(&self) -> (i8, i8) {
        use std::cmp::min;

        self.0.keys().into_iter().cloned()
            .fold((0, 0), |(mx, my), (ox, oy)| (min(mx, ox), min(my, oy)))
    }

    fn shifted(&self, (x, y): (i8, i8)) -> Self {
        self.modified(|(ox, oy)| (ox + x, oy + y))
    }

    fn merged(&self, other: &Board) -> Option<Self> {
        let mut hashmap = HashMap::new();
        hashmap.extend(other.0.iter());
        hashmap.extend(self.0.iter());

        if hashmap.len() != self.0.len() + other.0.len() {
            return None;
        }

        Some(Self(hashmap))
    }

    fn contained(&self, x: i8, y: i8) -> bool {
        self.0.keys().into_iter().cloned()
            .fold(true, |b, (ox, oy)| b && ox < x && oy < y && ox >= 0 && oy >= 0)
    }

    fn whole_lines(&self, x: i8, y: i8) -> Vec<i8> {
        let mut idxs = vec![];
        for oy in 0 .. y {
            if (0 .. x).filter_map(|ox| self.0.get(&(ox, oy))).count() == x as usize {
                idxs.push(oy)
            }
        }

        idxs
    }

    fn kill_line(&self, y: i8) -> Self {
        self.modified_filter(|(ox, oy)|
            if oy > y {
                Some((ox, oy))
            } else if oy == y {
                None
            } else {
                Some((ox, oy + 1))
            }
        )
    }

    fn render<'a, G>(
        &self,
        metrics: &Metrics,
        c: &Context,
        g: &mut G,
        draw_effect: DrawEffect<'a>,
    )
        where G: Graphics
    {
        let mut draw = |color, rect: [f64; 4]| {
            Rectangle::new(color).draw(rect, &DrawState::default(), c.transform, g);
        };

        for x in 0 .. metrics.board_x {
            for y in 0 .. metrics.board_y {
                let block_pixels = metrics.block_pixels as f64;
                let border_size = block_pixels / 20.0;
                let outer = [block_pixels * (x as f64), block_pixels * (y as f64), block_pixels, block_pixels];
                let inner = [outer[0] + border_size, outer[1] + border_size,
                outer[2] - border_size * 2.0, outer[3] - border_size * 2.0];

                draw([0.2, 0.2, 0.2, 1.0], outer);
                draw([0.1, 0.1, 0.1, 1.0], inner);

                if let Some(color) = self.0.get(&(x as i8, y as i8)) {
                    let code = match color {
                        Color::Red     => [1.0, 0.0, 0.0, 1.0],
                        Color::Green   => [0.0, 1.0, 0.0, 1.0],
                        Color::Blue    => [0.5, 0.5, 1.0, 1.0],
                        Color::Magenta => [1.0, 0.0, 1.0, 1.0],
                        Color::Cyan    => [0.0, 1.0, 1.0, 1.0],
                        Color::Yellow  => [1.0, 1.0, 0.0, 1.0],
                        Color::Orange  => [1.0, 0.5, 0.0, 1.0],
                    };
                    draw(code, outer);
                    let code = [code[0]*0.8, code[1]*0.8, code[2]*0.8, code[3]];
                    draw(code, inner);
                }

                match draw_effect {
                    DrawEffect::None => {},
                    DrawEffect::Flash(lines) => {
                        if lines.contains(&(y as i8)) {
                            draw([1.0, 1.0, 1.0, 0.5], outer);
                        }
                    }
                    DrawEffect::Darker => {
                        draw([0.0, 0.0, 0.0, 0.9], outer);
                    }
                }
            }
        }
    }
}

#[derive(Default)]
struct Metrics {
    block_pixels: usize,
    board_x: usize,
    board_y: usize,
}

impl Metrics {
    fn resolution(&self) -> [u32; 2] {
        [(self.board_x * self.block_pixels) as u32,
         (self.board_y * self.block_pixels) as u32]
    }
}

enum State {
    Flashing(isize, Instant, Vec<i8>),
    Falling(Board),
    GameOver,
}

struct Game {
    board: Board,
    metrics: Metrics,
    state: State,
    shift: (i8, i8),
    possible_pieces: Vec<Board>,
    time_since_fall: Instant,
}

impl Game {
    fn new(metrics: Metrics) -> Self {
        Self {
            metrics,
            board: Default::default(),
            state: State::Falling(Default::default()),
            time_since_fall: Instant::now(),
            shift: (0, 0),
            possible_pieces: vec![
                Board::new(&[(0, 0), (0, 1), (1, 0), (1, 1), ][..], Color::Red),
                Board::new(&[(0, 0), (1, 0), (1, 1), (2, 0), ][..], Color::Green),
                Board::new(&[(0, 0), (1, 0), (2, 0), (3, 0), ][..], Color::Blue),
                Board::new(&[(0, 0), (1, 0), (2, 0), (0, 1), ][..], Color::Orange),
                Board::new(&[(0, 0), (1, 0), (2, 0), (2, 1), ][..], Color::Yellow),
                Board::new(&[(0, 0), (1, 0), (1, 1), (2, 1), ][..], Color::Cyan),
                Board::new(&[(1, 0), (2, 0), (0, 1), (1, 1), ][..], Color::Magenta),
            ]
        }
    }

    fn new_falling(&mut self) {
        let mut rng = rand::thread_rng();
        let idx = rng.gen_range(0, self.possible_pieces.len());

        self.state = State::Falling(s
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

potato_potato_123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值