Rust教程:贪吃蛇游戏(第 2/2 部分)

In this article, we’ll finish the snake.rs file, and also continue with the rest of the files (main.rs, draw.rs, game.rs).
欢迎来到本教程的第二部分,在本文中,我们将完成 snake.rs 文件,并继续处理其余文件(main.rs、draw.rs、game.rs)。

查看第一部分:

Rust教程:贪吃蛇游戏(第 1/2 部分)-CSDN博客

snake.rs

As a reminder from the [1st part], we had finished working with the functions drawhead_position and move_forward in the snake.rs file.
作为[第 1 部分] 的提醒,我们已经完成了 Snake.rs 文件中的函数 draw 、 head_position 和 move_forward 的使用。

Functions: head_directionnext_headrestore_tail and overlap_tail
函数: head_direction 、 next_head 、 restore_tail 和 overlap_tail

Time to create a new function that will allow us to take in our snake or a reference to our snake and then get a direction.
是时候创建一个新函数了,它允许我们接收蛇或对蛇的引用,然后获得方向。

pub fn head_direction(&self) -> Direction {
    self.direction
}

Alright, so we want another method, I’m going to call it next_head. This will take in a reference to &self and an Option<Direction>, and then it will output a tuple of i32. So we'll say let (head_x, head_y): (i32, i 32) and then we'll get the head_position using our head_position method.
好吧,我们想要另一种方法,我将其命名为 next_head 。这将接受对 &self 和 Option<Direction> 的引用,然后输出 i32 的元组。因此,我们会说 let (head_x, head_y): (i32, i 32) ,然后我们将使用 head_position 方法获取 head_position 。

pub fn next_head(&self, dir: Option<Direction>) -> (i32, i32) {
        let (head_x, head_y): (i32, i32) = self.head_position();

        let mut moving_dir = self.direction;
        match dir {
            Some(d) => moving_dir = d,
            None => {}
        }

        match moving_dir {
            Direction::Up => (head_x, head_y - 1),
            Direction::Down => (head_x, head_y + 1),
            Direction::Left => (head_x - 1, head_y),
            Direction::Right => (head_x + 1, head_y),
        }
    }

We’ll get the snake direction with the mutable moving direction let mut moving_dir = self.direction; and then we're going to match on the direction that we're passing into the method.
我们将通过可变的移动方向 let mut moving_dir = self.direction; 获得蛇的方向,然后我们将在传递给方法的方向上 match 。

Then we’re going to match again on this new moving_dir, this will help with accuracy.
然后我们将在这个新的 moving_dir 上再次 match ,这将有助于提高准确性。

Finally, we have two more methods we want to create. Create another public function called restore_tail. It will take in a reference to our mutable Snake. We'll also create a block which will be based on our tail. Then we're going to push_back our cloned tail into the back of our body.
最后,我们还有两个要创建的方法。创建另一个名为 restore_tail 的公共函数。它将引用我们可变的 Snake。我们还将创建一个基于我们的尾巴的块。然后我们将 push_back 我们克隆的尾巴放入我们的身体后部。

Basicallyas you know the tail doesn’t get rendered unless we eat an apple. So if we eat an apple this method will be run and the tail will be pushed into our linked list body. This is how our snake is growing in size.
基本上,如你所知,除非我们吃苹果,否则尾巴不会被渲染。因此,如果我们吃一个苹果,该方法将运行,并且尾部将被推入我们的链表主体中。我们的蛇就是这样长大的。

   pub fn restore_tail(&mut self) {
        let blk = self.tail.clone().unwrap();
        self.body.push_back(blk);
    }

Last but not least, we have our last method for this file. Let’s call this method overlap_tail. It will take in our Snake an x and a y, then we will pass back a boolean.Let's also create a mutable value and set it to equal to zero. We'll iterate through our snake body and we'll check to see if x equals block.x and if y equals block.x. So in other words:
最后但并非最不重要的一点是,我们有这个文件的最后一个方法。我们将此方法称为 overlap_tail 。它将接受我们的 Snake 一个 x 和一个 y ,然后我们将传回一个 boolean 。我们还创建一个可变值并将其设置为等于零。我们将迭代我们的蛇体,并检查 x 是否等于 block.x 以及 y 是否等于 block.x 。换句话说:

  • If our snake is overlapping with any other part of its actual body then we’ll return true.
    如果我们的蛇与其实际身体的任何其他部分重叠,那么我们将 return true 。
  • Otherwise, we’re going to increment ch.
    否则,我们将增加 ch 。

Then we’re going to check if ch equals == self.body.len() - 1, what we're doing with this part of our method is checking to see if our snake is actually overpassing the tail. If the tail and the head overlap in the same block there is actually a moment where the head will be in that block and so will the tail and we don't want this to cause a failure state so we break.
然后我们将检查 ch 是否等于 == self.body.len() - 1 ,我们在这部分方法中所做的就是检查我们的蛇是否实际上越过了尾巴。如果尾部和头部在同一个块中重叠,实际上有一段时间头部将在该块中,尾部也会在该块中,我们不希望这导致失败状态,所以我们 break 。

 pub fn overlap_tail(&self, x: i32, y: i32) -> bool {
        let mut ch = 0;
        for block in &self.body {
            if x == block.x && y == block.y {
                return true;
            }

            ch += 1;
            if ch == self.body.len() - 1 {
                break;
            }
        }
        return false;
    }

That’s it for our snake file! Woohoo! Take a moment to reflect on the code we wrote so far, cause quite honestly we have a few more functions to write in the other files! 😊
这就是我们的蛇文件!呜呼!花点时间反思一下我们到目前为止编写的代码,因为老实说我们还有一些函数要在其他文件中编写! 😊

game.rs

Let’s go to the game.rs file. Same as with our other files we want to come into our main file and type mod game; to link it up with our project.
让我们转到 game.rs 文件。与我们的其他文件一样,我们希望进入 main 文件并输入 mod game; 将其与我们的项目链接起来。

Then, back on the game.rs files, we want to import all of the piston_window (that's why we'll use the asterisk).
然后,回到 game.rs 文件,我们要导入所有 piston_window (这就是我们使用星号的原因)。

We also want the random library and we want to get out thread_rng as it allows us to create a thread local random number generator (this way we're using our operating system to create a random number). We're also bringing in the Rng .
我们还需要 random 库,并且希望使用 thread_rng ,因为它允许我们创建一个线程本地随机数生成器(这样我们就可以使用我们的操作系统来创建一个随机数)。我们还引入了 Rng 。

use piston_window::types::Color; 
use piston_window::*; 

use rand::{thread_rng, Rng};

Then we also want to bring in our Snake direction and then the Snake itself.
然后我们还想引入蛇的方向,然后引入蛇本身。

use crate::snake::{Direction, Snake};

And we also want to bring in our Draw block and our Draw rectangle functions.
我们还想引入 Draw 块和 Draw 矩形函数。

use crate::draw::{draw_block, draw_rectangle};

We want to create 3 constants:
我们想要创建 3 个常量:

  • FOOD_COLOR: This will be red, so 0.8 and it will have an opacity of 1.
    FOOD_COLOR :这将是红色,因此为 0.8,并且不透明度为 1。
  • BORDER_COLOR: This will be completely black.
    BORDER_COLOR :这将是全黑的。
  • GAMEOVER_COLOR: This will be 0.9 so it will be red again, but it will have an opacity of 0.5.
    GAMEOVER_COLOR :这将是 0.9,因此它将再次变为红色,但其不透明度为 0.5。
const FOOD_COLOR: Color = [0.80, 0.00, 0.00, 1.0];
const BORDER_COLOR: Color = [0.00, 0.00, 0.00, 1.0];
const GAMEOVER_COLOR: Color = [0.90, 0.00, 0.00, 0.5];

Then we also want to create 2 other constants.
然后我们还想创建另外 2 个常量。

  • MOVING_PERIOD: This is essentially the frames per second that our snake will move at.
    MOVING_PERIOD :这本质上是我们的蛇移动的每秒帧数。
  • RESTART_TIME: The restart time is 1 second. When we hit a failure state with our snake this will pause the game for one second before resetting it. If you find this to be too fast you can fiddle around with it.
    RESTART_TIME :重启时间为1秒。当我们的蛇遇到失败状态时,这将使游戏暂停一秒钟,然后再重置。如果你发现这太快了,你可以摆弄它。
const MOVING_PERIOD: f64 = 0.1;
const RESTART_TIME: f64 = 1.0;

Alright, now we’re going to create a new struct called Game. This will have a snake in it but also the food which will be a boolean. If food_exists on the board then we don't need to spawn more. We'll have the food_x and food_y coordinates, and then we'll have the width and the height of the actual game board. Finally, we'll have the game state ( game_over) as a boolean and the waiting_time which is the restart time up.
好吧,现在我们要创建一个名为 Game 的新 struct 。里面会有一条蛇,还有 boolean 的食物。如果 food_exists 在棋盘上,那么我们不需要生成更多。我们将拥有 food_x 和 food_y 坐标,然后我们将拥有实际游戏板的 width 和 height 坐标。最后,我们将游戏状态 ( game_over ) 作为 boolean 和 waiting_time (重启时间到了)。

pub struct Game {
    snake: Snake,

    food_exists: bool,
    food_x: i32,
    food_y: i32,

    width: i32,
    height: i32,

    game_over: bool,
    waiting_time: f64,
}

Implementation block Game
实现块 Game

We want to make an implementation block for our game so we can create some methods. We’re going to create a new method so that we can instantiate a new game. This will take in the width and the height of the actual game board itself and then we'll output a Game which will then run the Snake::new(2,2) function (2,2 is 2 units out and 2 units down). Then our waiting_time will be 0 so the snake will automatically start moving. food_exists will be true so the food will spawn and it will spawn at this food_x and food_y. Then we have our width and height, these are the size of the board and then our game_over will be false. When the game is running this will be false and then once we hit a wall or we hit ourselves it will turn to true.
我们想为我们的游戏制作一个实现块,以便我们可以创建一些方法。我们将创建一个 new 方法,以便我们可以实例化一个新游戏。这将接收实际游戏板本身的 width 和 height ,然后我们将输出 Game ,然后运行 ​​ Snake::new(2,2) 将是 0 所以蛇会自动开始移动。 food_exists 将是 true ,因此食物将生成,并将在此 food_x 和 food_y 处生成。然后我们有 width 和 height ,它们是板的大小,然后我们的 game_over 将是 false 。当游戏运行时,这将是 false ,然后一旦我们撞到墙壁或撞到自己,它就会变成 true 。

impl Game {  
    pub fn new(width: i32, height: i32) -> Game {
        Game {
            snake: Snake::new(2, 2),
            waiting_time: 0.0,
            food_exists: true,
            food_x: 6,
            food_y: 4,
            width,
            height,
            game_over: false,
        }
    }

Now we want to create another method called key_pressed, this will allow us to figure out whether or not the user has pressed the key and then react accordingly. So key_pressed takes in a mutable game self and then it takes in a key type. If game_over then we want to just quit but if it's not then we want to match on key and:
现在我们要创建另一个名为 key_pressed 的方法,这将使我们能够确定用户是否按下了 key ,然后做出相应的反应。因此 key_pressed 接受可变游戏 self ,然后接受 key 类型。如果 game_over 那么我们只想退出,但如果不是那么我们想要 match 在 key 上并且:

  • If Key::Up => Some(Direction::Up) then we're going to go up.
    如果 Key::Up => Some(Direction::Up) 那么我们就会上升。
  • If Key::Down => Some(Direction::Down) then we're going to go down.
    如果 Key::Down => Some(Direction::Down) 那么我们就会下降。
  • Etc…

Then we’re going to check dir, if dir == self.snake.head_direction().opposite() then we're going to quit out of this function. So for example, if the snake is moving up and we try to hit down then nothing will happen.
然后我们将检查 dir ,如果 dir == self.snake.head_direction().opposite() 那么我们将退出这个函数。举例来说,如果蛇向上移动,而我们尝试向下击打,那么什么也不会发生。

  pub fn key_pressed(&mut self, key: Key) {
        if self.game_over {
            return;
        }

        let dir = match key {
            Key::Up => Some(Direction::Up),
            Key::Down => Some(Direction::Down),
            Key::Left => Some(Direction::Left),
            Key::Right => Some(Direction::Right),
            _ => Some(self.snake.head_direction()),
        };

        if let Some(dir) = dir {
            if dir == self.snake.head_direction().opposite() {
                return;
            }
        }

        self.update_snake(dir);
    }

Alright, as you can see above, in the last line, I have the self.update_snake(dir);, but we haven't written it yet. We'll do that pretty soon... Keep reading and coding with me.
好吧,正如你在上面看到的,在最后一行,我有 self.update_snake(dir); ,但我们还没有写它。我们很快就会做到这一点...继续和我一起阅读和编码。

Let’s create a public draw function. It will take in a reference to our game board, the context and our graphics buffer. First, we're going to call self.snake.draw and what this will do is to iterate through our linked list and then draw_block based on those linked lists. Then we're going to check and see if food_exists. If this comes back as true then we're going to draw_block with the FOOD_COLOR , self.food.x and self.food.y.
让我们创建一个公共 draw 函数。它将引用我们的游戏板、上下文和图形缓冲区。首先,我们将调用 self.snake.draw ,这将迭代我们的链接列表,然后基于这些链接列表迭代 draw_block 。然后我们将检查是否 food_exists 。如果返回为 true 那么我们将使用 FOOD_COLOR 、 self.food.x 和 self.food.y 来 draw_block 。

pub fn draw(&self, con: &Context, g: &mut G2d) {
        self.snake.draw(con, g);

        if self.food_exists {
            draw_block(FOOD_COLOR, self.food_x, self.food_y, con, g);
        }

        draw_rectangle(BORDER_COLOR, 0, 0, self.width, 1, con, g);
        draw_rectangle(BORDER_COLOR, 0, self.height - 1, self.width, 1, con, g);
        draw_rectangle(BORDER_COLOR, 0, 0, 1, self.height, con, g);
        draw_rectangle(BORDER_COLOR, self.width - 1, 0, 1, self.height, con, g);

        if self.game_over {
            draw_rectangle(GAMEOVER_COLOR, 0, 0, self.width, self.height, con, g);
        }
    }

Then we’re going to draw the borders and finally, we will run another check: if self.game_over then we want to draw the entire screen.
然后我们将绘制边框,最后,我们将运行另一项检查: if self.game_over 然后我们要绘制整个屏幕。

All right, now we’re going to make an update function. We'll pass our game state as a mutable and then a time ( delta_time: f64). Then we're going to iterate our waiting_time and if the game is over and if self.waiting_time > RESTART_TIME then restart the game. We'll use this function restart , we haven't written it yet, but keep it up and you'll write it soon with me! Otherwise, we're just going to return .
好吧,现在我们要创建一个 update 函数。我们将把游戏状态作为可变参数传递,然后传递时间 ( delta_time: f64 )。然后我们将迭代 waiting_time ,如果游戏结束,如果 self.waiting_time > RESTART_TIME 则重新启动游戏。我们将使用这个函数 restart ,我们还没有写它,但是继续坚持,你很快就会和我一起写它!否则,我们只会转到 return 。

If the food does not exist then we’re going to call the add_food method (we'll write it soon). Then we're going to update the snake ( update_snake~ see the function below).
如果食物不存在,那么我们将调用 add_food 方法(我们很快就会写)。然后我们将更新蛇( update_snake ~ 请参阅下面的函数)。

 pub fn update(&mut self, delta_time: f64) {
        self.waiting_time += delta_time;

        if self.game_over {
            if self.waiting_time > RESTART_TIME {
                self.restart();
            }
            return;
        }

        if !self.food_exists {
            self.add_food();
        }

        if self.waiting_time > MOVING_PERIOD {
            self.update_snake(None);
        }
    }

Now let’s check and see if the snake has eaten. We have a new function check_eating which takes the mutable game state. We're going to find the head_x and head_y of the head using our head_position method. Then we're going to check if the food_exists and if self.food_x == head_x && self.food_y == head_y. If the head overlaps with our food then we're going to say that food doesn't exist anymore (false) and call our restore_tail function. In other words, our snake is going to grow one block!
现在让我们检查一下蛇是否吃过东西。我们有一个新函数 check_eating ,它采用可变的游戏状态。我们将使用 head_position 方法找到头部的 head_x 和 head_y 。然后我们将检查是否 food_exists 和 self.food_x == head_x && self.food_y == head_y 。如果头部与我们的食物重叠,那么我们会说食物不再存在( false )并调用我们的 restore_tail 函数。换句话说,我们的蛇会长一格!

fn check_eating(&mut self) {
        let (head_x, head_y): (i32, i32) = self.snake.head_position();
        if self.food_exists && self.food_x == head_x && self.food_y == head_y {
            self.food_exists = false;
            self.snake.restore_tail();
        }
    }

Now we want to check if the snake is alive! We have a new function check_if_snake_alive and we pass in our reference to self and then an Option of Direction , we're also going to pass back a boolean. We're going to check if the snake head overlaps with the tail self.snake.overlap_tail(next_x, next_y) , in this case, we'll return false. If we go out of bounds of the window then the game will end and it will restart after a second.
现在我们要检查蛇是否还活着!我们有一个新函数 check_if_snake_alive ,我们传入对 self 的引用,然后传入 Direction 的 Option ,我们也将传回 boolean 。我们将检查蛇头是否与尾部重叠 self.snake.overlap_tail(next_x, next_y) ,在本例中,我们将 return false 。如果我们超出了窗口范围,那么游戏将结束并在一秒钟后重新开始。

    fn check_if_snake_alive(&self, dir: Option<Direction>) -> bool {
        let (next_x, next_y) = self.snake.next_head(dir);

        if self.snake.overlap_tail(next_x, next_y) {
            return false;
        }

        next_x > 0 && next_y > 0 && next_x < self.width - 1 && next_y < self.height - 1
    }

Now let’s actually add the food! The add_food is the method that we were calling in the update function. It takes a mutable game state and then we create an rng element and call our thread_rng . We'll check if the snake is overlapping with the tail (we don't want the snake to overall with the apple), and then we'll set the food_x and food_y and also the food_exists to true.
现在让我们实际添加食物! add_food 是我们在 update 函数中调用的方法。它需要一个可变的游戏状态,然后我们创建一个 rng 元素并调用 thread_rng 。我们将检查蛇是否与尾巴重叠(我们不希望蛇与苹果整体重叠),然后我们将设置 food_x 和 food_y 和还有 food_exists 到 true 。

 fn add_food(&mut self) {
        let mut rng = thread_rng();

        let mut new_x = rng.gen_range(1..self.width - 1);
        let mut new_y = rng.gen_range(1..self.height - 1);
        while self.snake.overlap_tail(new_x, new_y) {
            new_x = rng.gen_range(1..self.width - 1);
            new_y = rng.gen_range(1..self.height - 1);
        }

        self.food_x = new_x;
        self.food_y = new_y;
        self.food_exists = true;
    }

Perfect, we’re getting closer! We just need a few more functions.
完美,我们越来越近了!我们只需要更多的功能。

Let’s create the update_snake function which was mentioned above, in the update and key_pressed functions. We pass in our reference to self and then an Option of Direction. We'll check if the snake is alive, and if it is then we'll move_forward and check for eating, if it's not the game_over becomes true and we set the waiting_time to 0.0.
让我们在 update 和 key_pressed 函数中创建上面提到的 update_snake 函数。我们传入对 self 的引用,然后传入 Direction. 的 Option 我们将检查蛇是否还活着,如果是的话我们将< b6> 并检查是否吃东西,如果不是,则 game_over 变为 true ,我们将 waiting_time 设置为 0.0 。

  fn update_snake(&mut self, dir: Option<Direction>) {
        if self.check_if_snake_alive(dir) {
            self.snake.move_forward(dir);
            self.check_eating();
        } else {
            self.game_over = true;
        }
        self.waiting_time = 0.0;
    }

Let’s also write the restart method that we saw in the restart function. We pass in our reference to self and then we create a new Snake game, and set all the other parameters as well (like wating_timefood_exists, etc). This is very similar to the new function. The reason we don't call it it's because we don't want to render a new window every time the game resets!
我们还编写在 restart 函数中看到的 restart 方法。我们传入对 self 的引用,然后创建一个新的贪吃蛇游戏,并设置所有其他参数(如 wating_time 、 food_exists 等) 。这与 new 函数非常相似。我们不这么称呼它的原因是因为我们不想每次游戏重置时都渲染一个新窗口!

main.rs

Alright! Time to move on to main.rs.
好吧!是时候转到 main.rs 了。

Make sure you have imported the piston_window and the crates game and draw. We also want a CONST for BACK_COLOR (the color looks like gray):
确保您已导入 piston_window 以及包 game 和 draw 。我们还想要 BACK_COLOR 的 CONST (颜色看起来像灰色):

use piston_window::*; 
use piston_window::types::Color; 

use crate::game::Game; 
use crate::draw::to_coord_u32; 

const BACK_COLOR: Color = [0.5, 0.5, 0.5, 1.0];

Note the to_coord_u32 function. This is very similar to to_coord from draw.rs except here we don't want to return an f64 but a u32.
请注意 to_coord_u32 函数。这与 draw.rs 中的 to_coord 非常相似,只不过这里我们不想返回 f64 而是 u32 。

In the fn main() we'll get the width and the height and set it to (20, 20) (you can obviously set it to whatever you prefer), then we're going to create a mutable window which will be a PistonWindow and we'll create: a Snake game, a game window ([to_coord_u32(width), to_coord_u32(height)] ), we want to build the actual window and finally we have the unwrap to deal with any errors.
在 fn main() 中,我们将获取 width 和 height 并将其设置为 (20, 20) (显然,您可以将其设置为您想要的任何值)更喜欢),然后我们将创建一个可变窗口,它将是 PistonWindow 我们将创建:一个贪吃蛇游戏,一个游戏窗口( [to_coord_u32(width), to_coord_u32(height)] ),我们想要 build 实际窗口,最后我们有 unwrap 来处理任何错误。

fn main() {
    let (width, height) = (30, 30);

    let mut window: PistonWindow =
        WindowSettings::new("Snake", [to_coord_u32(width), to_coord_u32(height)])
            .exit_on_esc(true)
            .build()
            .unwrap();

    .
    .
    .
}

Then we’ll create a new Game with width and height. If the player presses a button, we're going to call the press_args and then pass a key in key_pressed , otherwise, we're going to draw_2d and pass in the event, clear the window and then draw the game.
然后我们将使用 width 和 height 创建一个新游戏。如果玩家按下按钮,我们将调用 press_args ,然后在 key_pressed 中传递 key ,否则,我们将 draw_2d 并传入事件, clear 窗口,然后 draw 游戏。

Lastly, we’re going to update the game with arg.dt.
最后,我们将使用 arg.dt update 进行游戏。

let mut game = Game::new(width, height);
    while let Some(event) = window.next() {
        if let Some(Button::Keyboard(key)) = event.press_args() {
            game.key_pressed(key);
        }
        window.draw_2d(&event, |c, g, _| {
            clear(BACK_COLOR, g);
            game.draw(&c, g);
        });

        event.update(|arg| {
            game.update(arg.dt);
        });
    }

That’s it, our game is finished! 👏👏
就这样,我们的游戏就完成了! 👏👏

Run the Game 运行游戏

You can run in your terminal cargo check to check if there are any errors and then cargo run to play the game! Enjoy and congrats on building it.
您可以在终端中运行 cargo check 检查是否有错误,然后 cargo run 开始玩游戏!享受并祝贺构建它。

Thank you for staying with me in this long, 2-parts, tutorial.
感谢您和我一起阅读这个由两部分组成的长教程。

第一部分:

Rust教程:贪吃蛇游戏(第 1/2 部分)-CSDN博客

Find the code here. 在这里找到代码。

EleftheriaBatsou/snake-game-rust (github.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/598019.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AWS宣布推出Amazon Q :针对商业数据和软件开发的生成性AI助手

亚马逊网络服务&#xff08;AWS&#xff09;近日宣布推出了一项名为“Amazon Q”的新服务&#xff0c;旨在帮助企业利用生成性人工智能&#xff08;AI&#xff09;技术&#xff0c;优化工作流程和提升业务效率。这一创新平台的推出&#xff0c;标志着企业工作方式的又一次重大变…

JetBrains DataSpell v2024.1 激活版 (数据科学家开发环境)

JetBrains系列软件安装目录 一、JetBrains IntelliJ IDEA v2024.1 安装教程 (Java集成开发IDE) 二、JetBrains WebStorm v2024.1 激活版 (JavaScript集成开发IDE) 三、JetBrains PhpStorm v2024.1 安装教程 (PHP集成开发IDE) 四、JetBrains PyCharm Pro v2024.1 安装教程 (…

burp靶场sql注入通关—下

第十一关&#xff08;布尔盲注&#xff09;&#xff1a; 1.根据提示修改包含 TrackingId cookie的请求&#xff0c;先抓包并修改这个值&#xff0c;在后面加上永真式发现出现Welcome back TrackingIdxxxx and 11 再修改这个值为永假式看看&#xff0c;发现没有Welcome back&am…

第二证券|A股,突现两大重磅!

大摩又看上新标的&#xff01; 就在刚才&#xff0c;五粮液和泸州老窖传来重磅音讯&#xff1a;摩根士丹利将泸州老窖目标价从194元上调至206元&#xff0c;将五粮液目标价从180元上调至182元。按这个目标价核算&#xff0c;五粮液还有20%的空间。而摩根士丹利最近似有一种魔力…

【触摸案例-手势解锁案例-按钮高亮 Objective-C语言】

一、我们来说这个self.btns,这个问题啊,为什么不用_btns, 1.我们说,在懒加载里边儿,经常是写下划线啊,_btns,为什么不写,首先啊,这个layoutSubviews:我们第一次,肯定会去执行这个layoutSubviews: 然后呢,去懒加载这个数组, 然后呢,接下来啊,走这一句话, 第一次…

rust容器、迭代器

一&#xff0c;std容器 1&#xff0c;Vec&#xff08;向量、栈&#xff09; use std::vec::Vec; &#xff08;1&#xff09;用作vector let nums:Vec<i32>vec![1,2,4,3];assert_eq!(nums.len(),4);assert_eq!(nums[3],3);assert_eq!(nums.is_empty(),false); 遍历&…

山西·长治大广赛赛题详解

大广赛新命题又又又又来啦&#xff0c;它就是山西长治&#xff0c;让我们一起来看看命题详情吧~ 2024年第16届全国大学生广告艺术大赛山西长治命题素材和资料下载&#xff1a; 命题素材下载https://js.design/f/ZRLbti?sourcecsdn&planbttss507 广告主题&#xff1a; 一…

美易官方:美股周一收高,道指连续第四个交易日上涨

收盘之际&#xff0c;美股市场周一的表现可圈可点&#xff0c;各大股指纷纷走高&#xff0c;道指更是连续第四个交易日实现上涨。这一积极态势不仅凸显了投资者对于全球经济的信心&#xff0c;也反映了市场对于未来前景的乐观预期。 道指涨176.59点&#xff0c;涨幅为0.46%&…

探索5个独特AI工具:它们是否值得独立存在?

在这个“地下AI”系列的最新一集中&#xff0c;我们深入挖掘了一些鲜为人知的AI工具。这些工具并非出自OpenAI、微软或谷歌等科技巨头之手&#xff0c;而是独立创造者和小型团队的智慧结晶。我们的目标是发现利用最新AI技术的独特工具。但这次有个新玩法&#xff1a;我们玩一个…

使用 OpenNJet 分分钟完成打地鼠小游戏部署

文章目录 OpenNJet应用引擎什么是应用引擎什么是OpenNJet独特优势技术架构 OpenNJet安装RPM安装 部署打地鼠小游戏配置OpenNJet部署打地鼠小游戏启动 NJet访问打地鼠小游戏 总结 今天阿Q打算使用OpenNJet应用引擎来部署一下我们的打地鼠小游戏。在开始部署之前&#xff0c;我们…

FME学习之旅---day26

我们付出一些成本&#xff0c;时间的或者其他&#xff0c;最终总能收获一些什么。 【由于上周&#xff0c;上班状态不是很好&#xff0c;事情多又杂&#xff0c;没有学习的劲头&#xff0c;就短暂的休息了一下下。双休爬山&#xff0c;给自己上了强度&#xff0c;今天才缓过来…

PMP培训一般要多久?

考过PMP很久了&#xff0c;学习时长还是记得很清楚的。因为有一部分的项目经验&#xff0c;报了威班PMP的培训&#xff0c;看了宣传是50天通过PMP&#xff0c;但是我仅仅用了一个月出头就搞定了&#xff0c;算下来才四十天不到就已经学完在准备冲刺参加考试了&#xff0c;最后5…

气膜冰球馆助力冰雪运动高速发展—轻空间

冰上运动一直备受人们热爱&#xff0c;其中冰球更是广受欢迎。近年来&#xff0c;随着技术的飞速发展&#xff0c;气膜冰球馆成为了冰上运动领域的新宠。本文将探讨气膜冰球馆在冰上运动中的重要性&#xff0c;以及其未来发展的前景。 气膜冰球馆具有明显的优势。相比传统冰球馆…

C#标签设计打印软件开发

1、新建自定义C#控件项目Custom using System; using System.Collections.Generic; using System.Text;namespace CustomControls {public class CommonSettings{/// <summary>/// 把像素换算成毫米/// </summary>/// <param name="Pixel">多少像素…

社交媒体数据恢复:华为畅连

尊敬的用户您好&#xff0c;以下是关于社交软件华为畅连的聊天记录数据恢复教程。在华为手机中&#xff0c;我们可以通过华为云服务和第三方软件来恢复删除的聊天记录。以下是详细的步骤&#xff1a; 第一步&#xff1a;登录华为云服务 请在您的华为手机上找到并打开“云服务”…

Learning C# Programming with Unity 3D

作者&#xff1a;Alex Okita 源码地址&#xff1a;GitHub - badkangaroo/UnityProjects: A repo for all of the projects found in the book. 全书 686 页。

实操专区-第11周-课堂练习专区-图的标记线和标记点

下载安装ECharts&#xff0c;完成如下样式图形。 代码和截图上传 完成 3.1.3.5 图的标记线和标记点 中的任务点 在一些折线图或柱状图当中&#xff0c;可以经常看到图中对最高值和最低值进行了标记。 在ECharts中&#xff0c;标记点&#xff08;markPoint&#xff09;常用于表示…

【YesPMP】众包平台5月7日最新外包项目

【YesPMP】众包平台5月7日最新外包项目&#xff0c;感兴趣的小伙伴&#xff0c;可进入平台参与竞标&#xff0c;竞标后与项目方直接与联系&#xff0c;双方直接对接。 1.查看项目&#xff1a;按照客户提供的设计稿美化页面&#xff0c;改html和js代码-YesPMP平台给你设计图&am…

使用免费的数据恢复软件通过简单的步骤恢复丢失的数据

犯错是人之常情&#xff01;您有时可能会意外地从PC或笔记本电脑中删除重要数据&#xff0c;旧的家庭或大学视频/照片&#xff0c;如果您面临数据丢失&#xff0c;则可以使用数据恢复软件轻松恢复丢失的数据。 奇客数据恢复软件可让您从笔记本电脑&#xff0c;PC和可移动存储设…

PowerShell ⇒ Excel 批量创建Excel

New-Object -ComObject Excel.Application&#xff1a;创建Excel对象[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null 用来显式释放 Excel COM 对象的资源&#xff0c;以确保在脚本执行完成后&#xff0c;释放 Excel 进程和相关资源&#xff0…
最新文章