いよいよ、コンソール画面型シューティングゲームの完成に向けて最後の調整です。
ここで、前回のソースコードと追加仕様を提示してプロンプトとしました。
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 ///
 
取扱いコース が豊富! 無料体験コースあります!
・Web制作&デザインコース
・Web制作コース
・Webデザインコース
・動画編集コース
・Pythonコース
・Javaコース
・Rubyコース
・Shopifyコース
————————————————————————————————————–
過去記事は下記を参照ください。2013年からミリタリー同人活動を行っています。
サイトマップ | ミリタリーグッズ工房「猫・工・艦」 (necokoucan.com)
【ブログ記事一覧】 | ミリタリーグッズ工房「猫・工・艦」 (necokoucan.com)
 ミリタリーランキング
ミリタリーランキング
 にほんブログ村
にほんブログ村Grid
** PR ** 目がかすむ、ぼやける。目の悩み解消解消に。


















![[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。] [商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]](https://hbb.afl.rakuten.co.jp/hgb/4682ef6f.d47e4f6a.4682ef70.b34ead15/?me_id=1397035&item_id=10000164&pc=https%3A%2F%2Fthumbnail.image.rakuten.co.jp%2F%400_mall%2Faokinomori%2Fcabinet%2F10634942%2Fimgrc0090394800.jpg%3F_ex%3D240x240&s=240x240&t=picttext)
