Rust言語でAIにプログラミングさせるシリーズ・コパイロットと作るゲームプログラミング・シューティングゲーム編3 No-R008


** PR ** 目がかすむ、ぼやける。目の悩み解消解消に。

                         

いよいよ、コンソール画面型シューティングゲームの完成に向けて最後の調整です。

ここで、前回のソースコードと追加仕様を提示してプロンプトとしました。

Rustでゲームを作ります。コードはこれを基本に
use std::io::{self, Write};
use std::thread;
use std::time::Duration;
use rand::Rng;
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};
use crossterm::{ExecutableCommand, cursor, terminal, event::{self, KeyCode, KeyEvent, Event}};

const WIDTH: usize = 10;
const HEIGHT: usize = 10;

fn main() {
let (tx, rx): (Sender, Receiver) = mpsc::channel();

// スレッドを作成してユーザー入力を非同期に処理
thread::spawn(move || {
    loop {
        if event::poll(Duration::from_millis(100)).unwrap() {
            if let Event::Key(key_event) = event::read().unwrap() {
                tx.send(Event::Key(key_event)).expect("送信に失敗しました");
            }
        }
    }
});

let mut player_pos = WIDTH / 2;
let mut enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
let mut bullets: Vec<(usize, usize)> = Vec::new();
let mut score = 0;

let mut stdout = io::stdout();
stdout.execute(terminal::Clear(terminal::ClearType::All)).unwrap();
stdout.execute(cursor::Hide).unwrap();

loop {
    // 画面をクリア
    stdout.execute(cursor::MoveTo(0, 0)).unwrap();

    // 敵の位置を更新
    enemy_pos.0 += 1;
    if enemy_pos.0 >= HEIGHT {
        enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
    }

    // 弾の位置を更新
    bullets.retain(|&(y, _)| y > 0);
    for bullet in &mut bullets {
        bullet.0 -= 1;
    }

    // 弾が敵に当たったかチェック
    if bullets.iter().any(|&bullet| bullet == enemy_pos) {
        score += 1;
        enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
        bullets.retain(|&bullet| bullet != enemy_pos);
    }

    // 敵とプレイヤーが衝突したかチェック
    if enemy_pos == (HEIGHT - 1, player_pos) {
        println!("ゲームオーバー!");
        break;
    }

    // 画面を描画
    for y in 0..HEIGHT {
        for x in 0..WIDTH {
            if (y, x) == (HEIGHT - 1, player_pos) {
                print!("P");
            } else if (y, x) == enemy_pos {
                print!("E");
            } else if bullets.contains(&(y, x)) {
                print!("|");
            } else {
                print!(".");
            }
        }
        println!();
    }

    println!("Score: {}", score);

    // プレイヤーの入力を処理
    if let Ok(event) = rx.try_recv() {
        if let Event::Key(KeyEvent { code, .. }) = event {
            match code {
                KeyCode::Char('a') => {
                    if player_pos > 0 {
                        player_pos -= 1;
                    }
                }
                KeyCode::Char('d') => {
                    if player_pos < WIDTH - 1 {
                        player_pos += 1;
                    }
                }
                KeyCode::Char(' ') => {
                    bullets.push((HEIGHT - 2, player_pos));
                }
                KeyCode::Char('q') => break,
                _ => {}
            }
        }
    }

    // 少し待つ
    thread::sleep(Duration::from_millis(100));
}

stdout.execute(cursor::Show).unwrap();

}

 ※ 

\|/  

イメージで半角で衝突したことを表すキャラクターとします。

以上をプロンプトとして投下しました。

コパイロット君の回答は。

面白いゲームですね!衝突を表すキャラクターを追加するために、以下のようにコードを変更してみてください。衝突時に「\|/」と「※」を表示するようにします。

Rust

use std::io::{self, Write};
use std::thread;
use std::time::Duration;
use rand::Rng;
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};
use crossterm::{ExecutableCommand, cursor, terminal, event::{self, KeyCode, KeyEvent, Event}};

const WIDTH: usize = 10;
const HEIGHT: usize = 10;

fn main() {
let (tx, rx): (Sender, Receiver) = mpsc::channel();

// スレッドを作成してユーザー入力を非同期に処理
thread::spawn(move || {
    loop {
        if event::poll(Duration::from_millis(100)).unwrap() {
            if let Event::Key(key_event) = event::read().unwrap() {
                tx.send(Event::Key(key_event)).expect("送信に失敗しました");
            }
        }
    }
});

let mut player_pos = WIDTH / 2;
let mut enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
let mut bullets: Vec<(usize, usize)> = Vec::new();
let mut score = 0;
let mut collision = false;

let mut stdout = io::stdout();
stdout.execute(terminal::Clear(terminal::ClearType::All)).unwrap();
stdout.execute(cursor::Hide).unwrap();

loop {
    // 画面をクリア
    stdout.execute(cursor::MoveTo(0, 0)).unwrap();

    // 敵の位置を更新
    enemy_pos.0 += 1;
    if enemy_pos.0 >= HEIGHT {
        enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
    }

    // 弾の位置を更新
    bullets.retain(|&(y, _)| y > 0);
    for bullet in &mut bullets {
        bullet.0 -= 1;
    }

    // 弾が敵に当たったかチェック
    if bullets.iter().any(|&bullet| bullet == enemy_pos) {
        score += 1;
        enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
        bullets.retain(|&bullet| bullet != enemy_pos);
    }

    // 敵とプレイヤーが衝突したかチェック
    if enemy_pos == (HEIGHT - 1, player_pos) {
        collision = true;
        break;
    }

    // 画面を描画
    for y in 0..HEIGHT {
        for x in 0..WIDTH {
            if (y, x) == (HEIGHT - 1, player_pos) {
                print!("P");
            } else if (y, x) == enemy_pos {
                print!("E");
            } else if bullets.contains(&(y, x)) {
                print!("|");
            } else {
                print!(".");
            }
        }
        println!();
    }

    println!("Score: {}", score);

    // プレイヤーの入力を処理
    if let Ok(event) = rx.try_recv() {
        if let Event::Key(KeyEvent { code, .. }) = event {
            match code {
                KeyCode::Char('a') => {
                    if player_pos > 0 {
                        player_pos -= 1;
                    }
                }
                KeyCode::Char('d') => {
                    if player_pos < WIDTH - 1 {
                        player_pos += 1;
                    }
                }
                KeyCode::Char(' ') => {
                    bullets.push((HEIGHT - 2, player_pos));
                }
                KeyCode::Char('q') => break,
                _ => {}
            }
        }
    }

    // 少し待つ
    thread::sleep(Duration::from_millis(100));
}

stdout.execute(cursor::Show).unwrap();

if collision {
    println!("\|/");
    println!(" ※ ");
    println!("ゲームオーバー!");
}

}

これで、敵とプレイヤーが衝突したときに「\|/」と「※」が表示されるようになります。ゲームの進行に合わせて、さらに改良を加えて楽しんでください!

追加のコードは、randとcrosstermクレートがプロジェクトに追加されていないためです。Cargo.tomlファイルにこれらの依存関係を追加する必要があります。

以下のようにCargo.tomlファイルを更新してください:

[dependencies]
rand = “0.8”
crossterm = “0.22”

これで、randとcrosstermクレートがプロジェクトに追加され、解消されるはずです。再度ビルドしてみてください。

\|/….
 ※ …..
ゲームオーバー!
……….
…|……
……….
…|……
…E……
…P……
Score: 0
という感じでPのプレイヤーの位置で
\|/….
 ※ …..と表示できれば完璧です。

AIがプロンプトを理解して「会話として回答してくる」このあたりが凄い

全角文字が原因で文字化けしてしまったのですね。半角文字で表示するように修正します。以下のようにコードを変更してください:

Rust

use std::io::{self, Write};
use std::thread;
use std::time::Duration;
use rand::Rng;
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};
use crossterm::{ExecutableCommand, cursor, terminal, event::{self, KeyCode, KeyEvent, Event}};

const WIDTH: usize = 10;
const HEIGHT: usize = 10;

fn main() {
    let (tx, rx): (Sender<Event>, Receiver<Event>) = mpsc::channel();

    // スレッドを作成してユーザー入力を非同期に処理
    thread::spawn(move || {
        loop {
            if event::poll(Duration::from_millis(100)).unwrap() {
                if let Event::Key(key_event) = event::read().unwrap() {
                    tx.send(Event::Key(key_event)).expect("送信に失敗しました");
                }
            }
        }
    });

    let mut player_pos = WIDTH / 2;
    let mut enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
    let mut bullets: Vec<(usize, usize)> = Vec::new();
    let mut score = 0;
    let mut collision = false;

    let mut stdout = io::stdout();
    stdout.execute(terminal::Clear(terminal::ClearType::All)).unwrap();
    stdout.execute(cursor::Hide).unwrap();

    loop {
        // 画面をクリア
        stdout.execute(cursor::MoveTo(0, 0)).unwrap();

        // 敵の位置を更新
        enemy_pos.0 += 1;
        if enemy_pos.0 >= HEIGHT {
            enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
        }

        // 弾の位置を更新
        bullets.retain(|&(y, _)| y > 0);
        for bullet in &mut bullets {
            bullet.0 -= 1;
        }

        // 弾が敵に当たったかチェック
        if bullets.iter().any(|&bullet| bullet == enemy_pos) {
            score += 1;
            enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
            bullets.retain(|&bullet| bullet != enemy_pos);
        }

        // 敵とプレイヤーが衝突したかチェック
        if enemy_pos == (HEIGHT - 1, player_pos) {
            collision = true;
            break;
        }

        // 画面を描画
        for y in 0..HEIGHT {
            for x in 0..WIDTH {
                if (y, x) == (HEIGHT - 1, player_pos) {
                    print!("P");
                } else if (y, x) == enemy_pos {
                    print!("E");
                } else if bullets.contains(&(y, x)) {
                    print!("|");
                } else {
                    print!(".");
                }
            }
            println!();
        }

        println!("Score: {}", score);

        // プレイヤーの入力を処理
        if let Ok(event) = rx.try_recv() {
            if let Event::Key(KeyEvent { code, .. }) = event {
                match code {
                    KeyCode::Char('a') => {
                        if player_pos > 0 {
                            player_pos -= 1;
                        }
                    }
                    KeyCode::Char('d') => {
                        if player_pos < WIDTH - 1 {
                            player_pos += 1;
                        }
                    }
                    KeyCode::Char(' ') => {
                        bullets.push((HEIGHT - 2, player_pos));
                    }
                    KeyCode::Char('q') => break,
                    _ => {}
                }
            }
        }

        // 少し待つ
        thread::sleep(Duration::from_millis(100));
    }

    stdout.execute(cursor::Show).unwrap();

    if collision {
        stdout.execute(cursor::MoveTo(HEIGHT as u16, 0)).unwrap();
        println!(r"\|/....");
        println!(" *  .....");
        println!("ゲームオーバー!");
    }
}

AI で生成されたコード。ご確認のうえ、慎重にご使用ください。FAQ で詳細を確認

これで、プレイヤーの位置で半角文字の「\|/」と「*」が表示されるようになります。再度ビルドして試してみてください!

| \\*// このキャラクタをPの位置で表示に変更ください。

了解しました!プレイヤーの位置で「|」と「\*//」を表示するようにコードを変更します。以下のように修正してください:

Rust

use std::io::{self, Write};
use std::thread;
use std::time::Duration;
use rand::Rng;
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};
use crossterm::{ExecutableCommand, cursor, terminal, event::{self, KeyCode, KeyEvent, Event}};

const WIDTH: usize = 10;
const HEIGHT: usize = 10;

fn main() {
    let (tx, rx): (Sender<Event>, Receiver<Event>) = mpsc::channel();

    // スレッドを作成してユーザー入力を非同期に処理
    thread::spawn(move || {
        loop {
            if event::poll(Duration::from_millis(100)).unwrap() {
                if let Event::Key(key_event) = event::read().unwrap() {
                    tx.send(Event::Key(key_event)).expect("送信に失敗しました");
                }
            }
        }
    });

    let mut player_pos = WIDTH / 2;
    let mut enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
    let mut bullets: Vec<(usize, usize)> = Vec::new();
    let mut score = 0;
    let mut collision = false;

    let mut stdout = io::stdout();
    stdout.execute(terminal::Clear(terminal::ClearType::All)).unwrap();
    stdout.execute(cursor::Hide).unwrap();

    loop {
        // 画面をクリア
        stdout.execute(cursor::MoveTo(0, 0)).unwrap();

        // 敵の位置を更新
        enemy_pos.0 += 1;
        if enemy_pos.0 >= HEIGHT {
            enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
        }

        // 弾の位置を更新
        bullets.retain(|&(y, _)| y > 0);
        for bullet in &mut bullets {
            bullet.0 -= 1;
        }

        // 弾が敵に当たったかチェック
        if bullets.iter().any(|&bullet| bullet == enemy_pos) {
            score += 1;
            enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
            bullets.retain(|&bullet| bullet != enemy_pos);
        }

        // 敵とプレイヤーが衝突したかチェック
        if enemy_pos == (HEIGHT - 1, player_pos) {
            collision = true;
            break;
        }

        // 画面を描画
        for y in 0..HEIGHT {
            for x in 0..WIDTH {
                if (y, x) == (HEIGHT - 1, player_pos) {
                    print!("P");
                } else if (y, x) == enemy_pos {
                    print!("E");
                } else if bullets.contains(&(y, x)) {
                    print!("|");
                } else {
                    print!(".");
                }
            }
            println!();
        }

        println!("Score: {}", score);

        // プレイヤーの入力を処理
        if let Ok(event) = rx.try_recv() {
            if let Event::Key(KeyEvent { code, .. }) = event {
                match code {
                    KeyCode::Char('a') => {
                        if player_pos > 0 {
                            player_pos -= 1;
                        }
                    }
                    KeyCode::Char('d') => {
                        if player_pos < WIDTH - 1 {
                            player_pos += 1;
                        }
                    }
                    KeyCode::Char(' ') => {
                        bullets.push((HEIGHT - 2, player_pos));
                    }
                    KeyCode::Char('q') => break,
                    _ => {}
                }
            }
        }

        // 少し待つ
        thread::sleep(Duration::from_millis(100));
    }

    stdout.execute(cursor::Show).unwrap();

    if collision {
        stdout.execute(cursor::MoveTo(HEIGHT as u16 - 1, player_pos as u16)).unwrap();
        println!("|");
        stdout.execute(cursor::MoveTo(HEIGHT as u16, player_pos as u16 - 1)).unwrap();
        println!("\\*//");
        println!("ゲームオーバー!");
    }
}

AI で生成されたコード。ご確認のうえ、慎重にご使用ください。FAQ で詳細を確認

これで、プレイヤーと敵が衝突したときに、プレイヤーの位置で「|」と「\*//」が表示されるようになります。再度ビルドして試してみてください!

残念です。
……….
..|…….*//
繧イ繝シ繝繧ェ繝シ繝舌・・・
PS C:\Users\\rust\sohtgun3>
……….
……….
……….
……….
..E…….
..P…….
Score: 0
となります。

文字化けが発生してしまったようですね。衝突時の表示を修正して、正しく表示されるようにします。以下のようにコードを変更してください:

Rust

use std::io::{self, Write};
use std::thread;
use std::time::Duration;
use rand::Rng;
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};
use crossterm::{ExecutableCommand, cursor, terminal, event::{self, KeyCode, KeyEvent, Event}};

const WIDTH: usize = 10;
const HEIGHT: usize = 10;

fn main() {
    let (tx, rx): (Sender<Event>, Receiver<Event>) = mpsc::channel();

    // スレッドを作成してユーザー入力を非同期に処理
    thread::spawn(move || {
        loop {
            if event::poll(Duration::from_millis(100)).unwrap() {
                if let Event::Key(key_event) = event::read().unwrap() {
                    tx.send(Event::Key(key_event)).expect("送信に失敗しました");
                }
            }
        }
    });

    let mut player_pos = WIDTH / 2;
    let mut enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
    let mut bullets: Vec<(usize, usize)> = Vec::new();
    let mut score = 0;
    let mut collision = false;

    let mut stdout = io::stdout();
    stdout.execute(terminal::Clear(terminal::ClearType::All)).unwrap();
    stdout.execute(cursor::Hide).unwrap();

    loop {
        // 画面をクリア
        stdout.execute(cursor::MoveTo(0, 0)).unwrap();

        // 敵の位置を更新
        enemy_pos.0 += 1;
        if enemy_pos.0 >= HEIGHT {
            enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
        }

        // 弾の位置を更新
        bullets.retain(|&(y, _)| y > 0);
        for bullet in &mut bullets {
            bullet.0 -= 1;
        }

        // 弾が敵に当たったかチェック
        if bullets.iter().any(|&bullet| bullet == enemy_pos) {
            score += 1;
            enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
            bullets.retain(|&bullet| bullet != enemy_pos);
        }

        // 敵とプレイヤーが衝突したかチェック
        if enemy_pos == (HEIGHT - 1, player_pos) {
            collision = true;
            break;
        }

        // 画面を描画
        for y in 0..HEIGHT {
            for x in 0..WIDTH {
                if (y, x) == (HEIGHT - 1, player_pos) {
                    print!("P");
                } else if (y, x) == enemy_pos {
                    print!("E");
                } else if bullets.contains(&(y, x)) {
                    print!("|");
                } else {
                    print!(".");
                }
            }
            println!();
        }

        println!("Score: {}", score);

        // プレイヤーの入力を処理
        if let Ok(event) = rx.try_recv() {
            if let Event::Key(KeyEvent { code, .. }) = event {
                match code {
                    KeyCode::Char('a') => {
                        if player_pos > 0 {
                            player_pos -= 1;
                        }
                    }
                    KeyCode::Char('d') => {
                        if player_pos < WIDTH - 1 {
                            player_pos += 1;
                        }
                    }
                    KeyCode::Char(' ') => {
                        bullets.push((HEIGHT - 2, player_pos));
                    }
                    KeyCode::Char('q') => break,
                    _ => {}
                }
            }
        }

        // 少し待つ
        thread::sleep(Duration::from_millis(100));
    }

    stdout.execute(cursor::Show).unwrap();

    if collision {
        stdout.execute(cursor::MoveTo(HEIGHT as u16 - 1, 0)).unwrap();
        println!("..........\n..|.......\n.\\*//......");
        println!("ゲームオーバー!");
    }
}

AI で生成されたコード。ご確認のうえ、慎重にご使用ください。FAQ で詳細を確認

これで、プレイヤーと敵が衝突したときに、プレイヤーの位置で「|」と「\*//」が正しく表示されるようになります。再度ビルドして試してみてください!

……………….
..|…….
.*//……
繧イ繝シ繝繧ェ繝シ繝舌・・・
PS C:\Users\\rust\sohtgun3> a
……….
……….
……….
E………
P………
Score: 0
どうも、衝突したタイミングで、Pの位置を取得し、そこから割り出し、Pの位置にを表示しその周りに他文字をイメージ通りに配置する配列 この配列の中心をPの位置と同期すればいいのでは?

その通りです!プレイヤーの位置を中心にして、衝突時に「|」と「\*//」を表示するように配列を使って配置する方法を試してみましょう。

以下のようにコードを修正してみてください:

Rust

use std::io::{self, Write};
use std::thread;
use std::time::Duration;
use rand::Rng;
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};
use crossterm::{ExecutableCommand, cursor, terminal, event::{self, KeyCode, KeyEvent, Event}};

const WIDTH: usize = 10;
const HEIGHT: usize = 10;

fn main() {
    let (tx, rx): (Sender<Event>, Receiver<Event>) = mpsc::channel();

    // スレッドを作成してユーザー入力を非同期に処理
    thread::spawn(move || {
        loop {
            if event::poll(Duration::from_millis(100)).unwrap() {
                if let Event::Key(key_event) = event::read().unwrap() {
                    tx.send(Event::Key(key_event)).expect("送信に失敗しました");
                }
            }
        }
    });

    let mut player_pos = WIDTH / 2;
    let mut enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
    let mut bullets: Vec<(usize, usize)> = Vec::new();
    let mut score = 0;
    let mut collision = false;

    let mut stdout = io::stdout();
    stdout.execute(terminal::Clear(terminal::ClearType::All)).unwrap();
    stdout.execute(cursor::Hide).unwrap();

    loop {
        // 画面をクリア
        stdout.execute(cursor::MoveTo(0, 0)).unwrap();

        // 敵の位置を更新
        enemy_pos.0 += 1;
        if enemy_pos.0 >= HEIGHT {
            enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
        }

        // 弾の位置を更新
        bullets.retain(|&(y, _)| y > 0);
        for bullet in &mut bullets {
            bullet.0 -= 1;
        }

        // 弾が敵に当たったかチェック
        if bullets.iter().any(|&bullet| bullet == enemy_pos) {
            score += 1;
            enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
            bullets.retain(|&bullet| bullet != enemy_pos);
        }

        // 敵とプレイヤーが衝突したかチェック
        if enemy_pos == (HEIGHT - 1, player_pos) {
            collision = true;
            break;
        }

        // 画面を描画
        for y in 0..HEIGHT {
            for x in 0..WIDTH {
                if (y, x) == (HEIGHT - 1, player_pos) {
                    print!("P");
                } else if (y, x) == enemy_pos {
                    print!("E");
                } else if bullets.contains(&(y, x)) {
                    print!("|");
                } else {
                    print!(".");
                }
            }
            println!();
        }

        println!("Score: {}", score);

        // プレイヤーの入力を処理
        if let Ok(event) = rx.try_recv() {
            if let Event::Key(KeyEvent { code, .. }) = event {
                match code {
                    KeyCode::Char('a') => {
                        if player_pos > 0 {
                            player_pos -= 1;
                        }
                    }
                    KeyCode::Char('d') => {
                        if player_pos < WIDTH - 1 {
                            player_pos += 1;
                        }
                    }
                    KeyCode::Char(' ') => {
                        bullets.push((HEIGHT - 2, player_pos));
                    }
                    KeyCode::Char('q') => break,
                    _ => {}
                }
            }
        }

        // 少し待つ
        thread::sleep(Duration::from_millis(100));
    }

    stdout.execute(cursor::Show).unwrap();

    if collision {
        let collision_pattern = [
            " | ",
            "\\*//"
        ];

        for (i, line) in collision_pattern.iter().enumerate() {
            stdout.execute(cursor::MoveTo((player_pos as u16).saturating_sub(1), (HEIGHT as u16).saturating_sub(2) + i as u16)).unwrap();
            println!("{}", line);
        }

        println!("ゲームオーバー!");
    }
}

AI で生成されたコード。ご確認のうえ、慎重にご使用ください。FAQ で詳細を確認

これで、プレイヤーと敵が衝突したときに、プレイヤーの位置で「|」と「\*//」が正しく表示されるようになります。再度ビルドして試してみてください!

仕様通りに完成ですが、
….*//..
の下に、たぶんゲームオーバーの日本語が表示?
だとしたら、これを英語ASCII文字にしてしまえばOKではGAME OVER!! とかかっこよい文字列で!

……….
……….
……….
……|…
……….
……|…
……….
……….
…. | …
….*//..
繧イ繝シ繝繧ェ繝シ繝舌・・・thread ‘
PS C:\Users\\rust\sohtgun3>

了解しました!「GAME OVER!!」のような英語のASCII文字を表示するように変更します。以下のようにコードを修正してください:

Rust

use std::io::{self, Write};
use std::thread;
use std::time::Duration;
use rand::Rng;
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};
use crossterm::{ExecutableCommand, cursor, terminal, event::{self, KeyCode, KeyEvent, Event}};

const WIDTH: usize = 10;
const HEIGHT: usize = 10;

fn main() {
    let (tx, rx): (Sender<Event>, Receiver<Event>) = mpsc::channel();

    // スレッドを作成してユーザー入力を非同期に処理
    thread::spawn(move || {
        loop {
            if event::poll(Duration::from_millis(100)).unwrap() {
                if let Event::Key(key_event) = event::read().unwrap() {
                    tx.send(Event::Key(key_event)).expect("送信に失敗しました");
                }
            }
        }
    });

    let mut player_pos = WIDTH / 2;
    let mut enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
    let mut bullets: Vec<(usize, usize)> = Vec::new();
    let mut score = 0;
    let mut collision = false;

    let mut stdout = io::stdout();
    stdout.execute(terminal::Clear(terminal::ClearType::All)).unwrap();
    stdout.execute(cursor::Hide).unwrap();

    loop {
        // 画面をクリア
        stdout.execute(cursor::MoveTo(0, 0)).unwrap();

        // 敵の位置を更新
        enemy_pos.0 += 1;
        if enemy_pos.0 >= HEIGHT {
            enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
        }

        // 弾の位置を更新
        bullets.retain(|&(y, _)| y > 0);
        for bullet in &mut bullets {
            bullet.0 -= 1;
        }

        // 弾が敵に当たったかチェック
        if bullets.iter().any(|&bullet| bullet == enemy_pos) {
            score += 1;
            enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
            bullets.retain(|&bullet| bullet != enemy_pos);
        }

        // 敵とプレイヤーが衝突したかチェック
        if enemy_pos == (HEIGHT - 1, player_pos) {
            collision = true;
            break;
        }

        // 画面を描画
        for y in 0..HEIGHT {
            for x in 0..WIDTH {
                if (y, x) == (HEIGHT - 1, player_pos) {
                    print!("P");
                } else if (y, x) == enemy_pos {
                    print!("E");
                } else if bullets.contains(&(y, x)) {
                    print!("|");
                } else {
                    print!(".");
                }
            }
            println!();
        }

        println!("Score: {}", score);

        // プレイヤーの入力を処理
        if let Ok(event) = rx.try_recv() {
            if let Event::Key(KeyEvent { code, .. }) = event {
                match code {
                    KeyCode::Char('a') => {
                        if player_pos > 0 {
                            player_pos -= 1;
                        }
                    }
                    KeyCode::Char('d') => {
                        if player_pos < WIDTH - 1 {
                            player_pos += 1;
                        }
                    }
                    KeyCode::Char(' ') => {
                        bullets.push((HEIGHT - 2, player_pos));
                    }
                    KeyCode::Char('q') => break,
                    _ => {}
                }
            }
        }

        // 少し待つ
        thread::sleep(Duration::from_millis(100));
    }

    stdout.execute(cursor::Show).unwrap();

    if collision {
        let collision_pattern = [
            " | ",
            "\\*//"
        ];

        for (i, line) in collision_pattern.iter().enumerate() {
            stdout.execute(cursor::MoveTo((player_pos as u16).saturating_sub(1), (HEIGHT as u16).saturating_sub(2) + i as u16)).unwrap();
            println!("{}", line);
        }

        stdout.execute(cursor::MoveTo(HEIGHT as u16, 0)).unwrap();
        println!("GAME OVER!!");
    }
}

AI で生成されたコード。ご確認のうえ、慎重にご使用ください。FAQ で詳細を確認

これで、プレイヤーと敵が衝突したときに、「|」と「\*//」の下に「GAME OVER!!」が表示されるようになります。再度ビルドして試してみてください!

残念
………
a a
……….
…….|..
……….
……..|.
……..|.
……….
…….. |
……..*//
Score: 21
ゲームオーバの時点で、Score:の下の行にカーソルを移動、そこに
.GAME OVER!!
PS C:\Users\mz808\rust\sohtgun3>
の表示でいいのでは?

了解しました!ゲームオーバーの時点で、スコアの下に「GAME OVER!!」を表示するようにコードを修正します。以下のように変更してください:

Rust

use std::io::{self, Write};
use std::thread;
use std::time::Duration;
use rand::Rng;
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};
use crossterm::{ExecutableCommand, cursor, terminal, event::{self, KeyCode, KeyEvent, Event}};

const WIDTH: usize = 10;
const HEIGHT: usize = 10;

fn main() {
    let (tx, rx): (Sender<Event>, Receiver<Event>) = mpsc::channel();

    // スレッドを作成してユーザー入力を非同期に処理
    thread::spawn(move || {
        loop {
            if event::poll(Duration::from_millis(100)).unwrap() {
                if let Event::Key(key_event) = event::read().unwrap() {
                    tx.send(Event::Key(key_event)).expect("送信に失敗しました");
                }
            }
        }
    });

    let mut player_pos = WIDTH / 2;
    let mut enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
    let mut bullets: Vec<(usize, usize)> = Vec::new();
    let mut score = 0;
    let mut collision = false;

    let mut stdout = io::stdout();
    stdout.execute(terminal::Clear(terminal::ClearType::All)).unwrap();
    stdout.execute(cursor::Hide).unwrap();

    loop {
        // 画面をクリア
        stdout.execute(cursor::MoveTo(0, 0)).unwrap();

        // 敵の位置を更新
        enemy_pos.0 += 1;
        if enemy_pos.0 >= HEIGHT {
            enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
        }

        // 弾の位置を更新
        bullets.retain(|&(y, _)| y > 0);
        for bullet in &mut bullets {
            bullet.0 -= 1;
        }

        // 弾が敵に当たったかチェック
        if bullets.iter().any(|&bullet| bullet == enemy_pos) {
            score += 1;
            enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
            bullets.retain(|&bullet| bullet != enemy_pos);
        }

        // 敵とプレイヤーが衝突したかチェック
        if enemy_pos == (HEIGHT - 1, player_pos) {
            collision = true;
            break;
        }

        // 画面を描画
        for y in 0..HEIGHT {
            for x in 0..WIDTH {
                if (y, x) == (HEIGHT - 1, player_pos) {
                    print!("P");
                } else if (y, x) == enemy_pos {
                    print!("E");
                } else if bullets.contains(&(y, x)) {
                    print!("|");
                } else {
                    print!(".");
                }
            }
            println!();
        }

        println!("Score: {}", score);

        // プレイヤーの入力を処理
        if let Ok(event) = rx.try_recv() {
            if let Event::Key(KeyEvent { code, .. }) = event {
                match code {
                    KeyCode::Char('a') => {
                        if player_pos > 0 {
                            player_pos -= 1;
                        }
                    }
                    KeyCode::Char('d') => {
                        if player_pos < WIDTH - 1 {
                            player_pos += 1;
                        }
                    }
                    KeyCode::Char(' ') => {
                        bullets.push((HEIGHT - 2, player_pos));
                    }
                    KeyCode::Char('q') => break,
                    _ => {}
                }
            }
        }

        // 少し待つ
        thread::sleep(Duration::from_millis(100));
    }

    stdout.execute(cursor::Show).unwrap();

    if collision {
        let collision_pattern = [
            " | ",
            "\\*//"
        ];

        for (i, line) in collision_pattern.iter().enumerate() {
            stdout.execute(cursor::MoveTo((player_pos as u16).saturating_sub(1), (HEIGHT as u16).saturating_sub(2) + i as u16)).unwrap();
            println!("{}", line);
        }

        stdout.execute(cursor::MoveTo(HEIGHT as u16 + 1, 0)).unwrap();
        println!("GAME OVER!!");
    }
}

AI で生成されたコード。ご確認のうえ、慎重にご使用ください。FAQ で詳細を確認

これで、ゲームオーバーの時点でスコアの下に「GAME OVER!!」が表示されるようになります。再度ビルドして試してみてください!

………. GAME OVER!!
PS C:\Users\\rust\sohtgun3>
……….
……….
……….
……….
……….
……….
…. | …
….*//..
Score: 0

カーソル位置の取得がうまくいっていないようですね。スコアの下に「GAME OVER!!」を表示するために、カーソルの位置を正確に設定する必要があります。以下のようにコードを修正してみてください:

Rust

use std::io::{self, Write};
use std::thread;
use std::time::Duration;
use rand::Rng;
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};
use crossterm::{ExecutableCommand, cursor, terminal, event::{self, KeyCode, KeyEvent, Event}};

const WIDTH: usize = 10;
const HEIGHT: usize = 10;

fn main() {
    let (tx, rx): (Sender<Event>, Receiver<Event>) = mpsc::channel();

    // スレッドを作成してユーザー入力を非同期に処理
    thread::spawn(move || {
        loop {
            if event::poll(Duration::from_millis(100)).unwrap() {
                if let Event::Key(key_event) = event::read().unwrap() {
                    tx.send(Event::Key(key_event)).expect("送信に失敗しました");
                }
            }
        }
    });

    let mut player_pos = WIDTH / 2;
    let mut enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
    let mut bullets: Vec<(usize, usize)> = Vec::new();
    let mut score = 0;
    let mut collision = false;

    let mut stdout = io::stdout();
    stdout.execute(terminal::Clear(terminal::ClearType::All)).unwrap();
    stdout.execute(cursor::Hide).unwrap();

    loop {
        // 画面をクリア
        stdout.execute(cursor::MoveTo(0, 0)).unwrap();

        // 敵の位置を更新
        enemy_pos.0 += 1;
        if enemy_pos.0 >= HEIGHT {
            enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
        }

        // 弾の位置を更新
        bullets.retain(|&(y, _)| y > 0);
        for bullet in &mut bullets {
            bullet.0 -= 1;
        }

        // 弾が敵に当たったかチェック
        if bullets.iter().any(|&bullet| bullet == enemy_pos) {
            score += 1;
            enemy_pos = (0, rand::thread_rng().gen_range(0..WIDTH));
            bullets.retain(|&bullet| bullet != enemy_pos);
        }

        // 敵とプレイヤーが衝突したかチェック
        if enemy_pos == (HEIGHT - 1, player_pos) {
            collision = true;
            break;
        }

        // 画面を描画
        for y in 0..HEIGHT {
            for x in 0..WIDTH {
                if (y, x) == (HEIGHT - 1, player_pos) {
                    print!("P");
                } else if (y, x) == enemy_pos {
                    print!("E");
                } else if bullets.contains(&(y, x)) {
                    print!("|");
                } else {
                    print!(".");
                }
            }
            println!();
        }

        println!("Score: {}", score);

        // プレイヤーの入力を処理
        if let Ok(event) = rx.try_recv() {
            if let Event::Key(KeyEvent { code, .. }) = event {
                match code {
                    KeyCode::Char('a') => {
                        if player_pos > 0 {
                            player_pos -= 1;
                        }
                    }
                    KeyCode::Char('d') => {
                        if player_pos < WIDTH - 1 {
                            player_pos += 1;
                        }
                    }
                    KeyCode::Char(' ') => {
                        bullets.push((HEIGHT - 2, player_pos));
                    }
                    KeyCode::Char('q') => break,
                    _ => {}
                }
            }
        }

        // 少し待つ
        thread::sleep(Duration::from_millis(100));
    }

    stdout.execute(cursor::Show).unwrap();

    if collision {
        let collision_pattern = [
            " | ",
            "\\*//"
        ];

        for (i, line) in collision_pattern.iter().enumerate() {
            stdout.execute(cursor::MoveTo((player_pos as u16).saturating_sub(1), (HEIGHT as u16).saturating_sub(2) + i as u16)).unwrap();
            println!("{}", line);
        }

        // スコアの下にカーソルを移動して「GAME OVER!!」を表示
        stdout.execute(cursor::MoveTo(0, (HEIGHT + 2) as u16)).unwrap();
        println!("GAME OVER!!");
    }
}

AI で生成されたコード。ご確認のうえ、慎重にご使用ください。FAQ で詳細を確認

これで、ゲームオーバーの時点でスコアの下に「GAME OVER!!」が表示されるようになります。再度ビルドして試してみてください!

無事見事に仕様どおりに完成です。本当に素晴らしいと思います。

素晴らしいですね!ゲームが無事に動作して、衝突時の表示も期待通りになったようで良かったです。おめでとうございます!

他に何かお手伝いできることがあれば、いつでも教えてくださいね。楽しんでゲーム開発を続けてください!

—————————————————————————————————————

//// PR ///

未経験からプロを目指せるオンラインプログラミングスクール【忍者CODE】

取扱いコース が豊富! 無料体験コースあります!

・Web制作&デザインコース

・Web制作コース

・Webデザインコース

・動画編集コース

・Pythonコース

・Javaコース

・Rubyコース

・Shopifyコース

————————————————————————————————————–

過去記事は下記を参照ください。2013年からミリタリー同人活動を行っています。

サイトマップ | ミリタリーグッズ工房「猫・工・艦」 (necokoucan.com)

【ブログ記事一覧】 | ミリタリーグッズ工房「猫・工・艦」 (necokoucan.com)

ミリタリーランキング
ブログランキング・にほんブログ村へにほんブログ村
広告

BASEオリジナルグッズ

PR VTuberサプーが教える! Python 初心者のコード/プロのコード

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です