ゲーム実装で身に付くプログラミング 3

タイトル画面をクリックして画面遷移する

タイトル画面をクリックして画面遷移する

タイトル画面をクリックしたら、図3のように色ブロックを表示したメインループ画面に遷移させます。ゲームステートが「Title」の場合、OnEnter で「reset_for_title」関数を実行し、更新(Update)ごとに「title_system」関数を実行します。ゲームステートが「MainLoop」なら入り口(OnEnter)で「start_stage」関数を実行します。

図3:色ブロックを表示

・サンプルコード「main.rs」ファイル

(前略)
// メイン
fn main() {
 App::new()
   .add_plugins(DefaultPlugins.set(WindowPlugin {
     primary_window: Some(Window {
       title: "Color".into(),
       ..default()
     }),
     ..default()
   }))
   .init_state::<GameState>()
   .init_resource::<GameData>()
   .insert_resource(ClearColor(Color::from(WHITE)))
   .add_systems(Startup, setup)
   // Title
   .add_systems(OnEnter(GameState::Title), reset_for_title)
   .add_systems(Update, title_system.run_if(in_state(GameState::Title)))
   // MainLoop
   .add_systems(OnEnter(GameState::MainLoop), start_stage)
   .run();
}
// 初期化
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
 commands.spawn(Camera2d);
 commands.spawn((
   Sprite::from_image(asset_server.load("images/Title.png")),
   Transform::from_xyz(0.0, 0.0, 10.0),
   TitleVisual,
 ));
 for i in 0..50 {
   let (c_type, path, tint) = match i % 3 {
     0 => (ColorType::Red, "images/0.png", RED),
     1 => (ColorType::Green, "images/1.png", LIME),
     _ => (ColorType::Blue, "images/2.png", BLUE),
   };
   commands.spawn((
     Sprite {
       image: asset_server.load(path),
       color: Color::from(tint),
       ..default()
     },
     Visibility::Hidden,
     Transform::default(),
     ColorItem {
       color_type: c_type,
       velocity: Vec2::ZERO,
     },
   ));
 }
}
// タイトル
fn title_system(
 mouse: Res<ButtonInput<MouseButton>>,
 mut next_state: ResMut<NextState<GameState>>,
) {
 if mouse.just_pressed(MouseButton::Left) {
   next_state.set(GameState::MainLoop);
 }
}
// タイトル画像表示
fn reset_for_title(
 mut title_q: Query<&mut Visibility, With<TitleVisual>>,
 mut item_q: Query<&mut Visibility, (With<ColorItem>, Without<TitleVisual>)>,
) {
 if let Ok(mut v) = title_q.single_mut() {
   *v = Visibility::Visible;
 }
 for mut v in &mut item_q {
   *v = Visibility::Hidden;
 }
}
// Stage開始
fn start_stage(
 mut game_data: ResMut<GameData>,
 mut title_q: Query<&mut Visibility, With<TitleVisual>>,
 mut item_q: Query<(&mut Visibility, &mut Transform, &mut ColorItem), Without<TitleVisual>>,
) {
 if let Ok(mut v) = title_q.single_mut() {
   *v = Visibility::Hidden;
 }
 let mut rng = rand::rng();
 let active_count = (game_data.stage * 3).min(50);
 for (i, (mut vis, mut trans, mut item)) in item_q.iter_mut().enumerate() {
   if i < active_count {
     *vis = Visibility::Visible;
     trans.translation = Vec3::new(
       rng.random_range(-450.0..450.0),
       rng.random_range(-280.0..280.0),
       1.0,
     );
     item.velocity = Vec2::new(
       rng.random_range(-0.2..0.2),
       rng.random_range(-0.2..0.2),
     );
   } else {
     *vis = Visibility::Hidden;
   }
 }
}

【サンプルコードの解説】
ECSのSystemでreset_for_title関数、title_system関数、start_stage関数を追加します。
「setup」関数で、アセットから「images/0.png」(Red列挙子を指定)、「images/1.png」(Green列挙子を指定)、「images/2.png」(Blue列挙子を指定)をスポーンします。
title_system関数でマウスがクリックされたらゲームステートをMainLoopにセットします。
reset_for_title関数で色ブロックを全て消し、タイトル画像を表示設定します。
start_stage関数で、ゲーム開始時にランダムな位置にランダムな「velocity(移動速度)」の色ブロックをstageの3倍(3色ずつ)表示します。

色ブロックをクリックして消す

やっとゲームらしく色ブロックを移動し、クリックした色ブロックを消します。ゲームステートがMainLoopの時、更新ごと(Update)に「movement_system」関数と「input_system」関数を呼び出します。

・サンプルコード「main.rs」ファイル

(前略)
// メイン
fn main() {
 App::new()
(中略)
   .add_systems(
     Update,
     (
       movement_system,
       input_system,
     )
     .run_if(in_state(GameState::MainLoop)),
   )
   .run();
}
// 初期化
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
(中略)
}
// タイトル
fn title_system(
 mouse: Res<ButtonInput<MouseButton>>,
 mut next_state: ResMut<NextState<GameState>>,
) {
(中略)
}
// タイトル画像表示
fn reset_for_title(
 mut title_q: Query<&mut Visibility, With<TitleVisual>>,
 mut item_q: Query<&mut Visibility, (With<ColorItem>, Without<TitleVisual>)>,
) {
(中略)
}
// Stage開始
fn start_stage(
 mut game_data: ResMut<GameData>,
 mut title_q: Query<&mut Visibility, With<TitleVisual>>,
 mut item_q: Query<(&mut Visibility, &mut Transform, &mut ColorItem), Without<TitleVisual>>,
) {
(中略)
}
// 色ブロック移動
fn movement_system(
 mut query: Query<(&mut Transform, &ColorItem), With<Visibility>>,
) {
 for (mut trans, item) in &mut query {
   trans.translation += item.velocity.extend(0.0);
 }
}
// クリック処理
fn input_system(
 mouse: Res<ButtonInput<MouseButton>>,
 windows: Query<&Window, With<PrimaryWindow>>,
 camera_q: Query<(&Camera, &GlobalTransform)>,
 mut item_q: Query<(&mut Visibility, &Transform, &ColorItem)>,
) {
 if !mouse.just_pressed(MouseButton::Left) {
   return;
 }
 let window = windows.single().unwrap();
 let (camera, camera_transform) = camera_q.single().unwrap();
 if let Some(world_pos) = window
   .cursor_position()
   .and_then(|c| camera.viewport_to_world_2d(camera_transform, c).ok())
 {
   for (mut vis, trans, _item) in &mut item_q {
     if *vis == Visibility::Visible
       && trans.translation.truncate().distance(world_pos) < SPRITE_SIZE
     {
       *vis = Visibility::Hidden;
       break;
     }
   }
 }
}

【サンプルコードの解説】
main関数では「App」構造体のコンストラクタでSystemにmovement_system 関数とinput_system関数を追加します。

movement_system関数で色ブロックをvelocityだけトランスレート(座標変換)して移動します。
input_system関数でクリックした時、マウスカーソルの位置を2Dゲーム画面の座標に変換します。色ブロックが画面外に出たら非表示にします。

【コラム】株専用パソコン

ゲーム専用機があるように、「たまごっち」があるように、株式投資専用機があればいいです。そうすれば安全に株式投資が楽しめるのではないでしょうか?調べてみるとプロ用の株OSが月額数10万円であるそうです。個人向けにその1/10の値段で作れば世界中で売れないでしょうか?

この記事をシェアしてください

人気記事トップ10

人気記事ランキングをもっと見る

企画広告も役立つ情報バッチリ! Sponsored