WinFormsでUnityのライフサイクルをシミュレートする
1. はじめに
Unityのオブジェクトは、特定のタイミングで実行される 「ライフサイクルイベント」 によって動作します。本チュートリアルでは、Windowsフォーム(WinForms)のイベントを利用して、Unityのライフサイクル(Awake → Start → Update → FixedUpdate → LateUpdate)をシミュレート します。
2. Unityのライフサイクルとは?
Unityのライフサイクルイベントは、オブジェクトの生成から削除まで、以下の順序で実行されます。
Unityのライフサイクル | 説明 |
---|---|
Awake | オブジェクトが生成されたときに1回実行(初期化) |
Start | オブジェクトがアクティブになった直後に1回実行 |
Update | 毎フレーム(約16msごと)に実行 |
FixedUpdate | 物理演算のために固定間隔(例:50ms)で実行 |
LateUpdate | Update の後に実行(後処理) |
OnDestroy | オブジェクトが削除されるときに実行 |
この流れを WinFormsのイベントとタイマーを活用 して再現していきます。
3. 開発環境の準備
3.1 必要なもの
- Visual Studio 2019 / 2022
- .NET 6+ または .NET Framework
- C#(Windows Formsアプリ)
3.2 プロジェクトの作成
- Visual Studio を開く
- 「新しいプロジェクトの作成」を選択
- 「Windows フォーム アプリ (.NET または .NET Framework)」を選択
- 任意のプロジェクト名を入力し、作成
4. UnityライフサイクルをWinFormsでシミュレート
4.1 フォームのイベントを登録
Unityのライフサイクルに相当する処理を、WinFormsのイベントに紐付ける必要があります。
以下の対応表を参考にしてください。
Unityのライフサイクル | WinFormsのイベント | 説明 |
---|---|---|
Awake | Form.Load | フォームが読み込まれたときに実行 |
Start | Form.Shown | フォームが表示された直後に実行 |
Update | Timer.Tick | 毎フレームごとに実行 |
FixedUpdate | Timer.Tick | 物理演算用の別のタイマーで実行 |
LateUpdate | Update の後に手動呼び出し | Update の後に実行 |
OnDestroy | Form.FormClosing | フォームが閉じられるときに実行 |
4.2 フォームのイベントを設定する
フォームのデザイン画面から、以下の手順でイベントを登録します。
1. MainForm
のデザイン画面を開く
- ソリューションエクスプローラーでForm1.csを右クリックし、名前の変更でMainForm.csに変更
- ソリューションエクスプローラー で
MainForm.cs
をダブルクリック
2. Load
イベントを設定
- フォーム上の空白部分をクリック
- プロパティウィンドウ の 「イベント」タブ(⚡マーク) を開く
Load
イベントを見つけてMainForm_Load
と入力(またはダブルクリック)
3. Shown
イベントを設定
- 同様に
Shown
イベントを見つけ、MainForm_Shown
と入力(またはダブルクリック)
4. FormClosing
イベントを設定
FormClosing
イベントを見つけ、MainForm_FormClosing
と入力(またはダブルクリック)
5. コードにイベントハンドラを追加
以下のコードを MainForm.cs
に記述します。
4.3 コードの記述
using System;
using System.Windows.Forms;
using System.Diagnostics;
using Timer = System.Windows.Forms.Timer;
namespace UnityLifecycleSimulation
{
public partial class MainForm : Form
{
private Timer updateTimer; // Updateのためのタイマー(毎フレーム実行)
private Timer fixedUpdateTimer; // FixedUpdateのためのタイマー(一定間隔で実行)
private Stopwatch stopwatch; // 経過時間を測るためのストップウォッチ
private bool startCalled = false; // Startが一度だけ実行されるようにするためのフラグ
public MainForm()
{
InitializeComponent();
Text = "Unityライフサイクルシミュレーション"; // ウィンドウタイトルの設定
}
// フォームがロードされた時に呼ばれる(UnityのAwake相当)
private void MainForm_Load(object sender, EventArgs e)
{
Awake();
}
// フォームが表示された後に呼ばれる(UnityのStart相当)
private void MainForm_Shown(object sender, EventArgs e)
{
// 少し遅延させてStartを呼び出す(即座に呼ばれないように)
Timer startTimer = new Timer { Interval = 10 };
startTimer.Tick += (s, ev) =>
{
startTimer.Stop();
Start();
};
startTimer.Start();
}
// UnityのAwake相当: 初期化処理
private void Awake()
{
Debug.WriteLine("[Awake] 初期化処理を実行しました");
MessageBox.Show("Awake: 初期化処理が実行されました", "Unityライフサイクル", MessageBoxButtons.OK, MessageBoxIcon.Information);
stopwatch = new Stopwatch();
stopwatch.Start();
// Update用タイマー(約60FPS)
updateTimer = new Timer { Interval = 16 };
updateTimer.Tick += Update;
// FixedUpdate用タイマー(物理計算向け、50msごとに実行)
fixedUpdateTimer = new Timer { Interval = 50 };
fixedUpdateTimer.Tick += FixedUpdate;
}
// UnityのStart相当: ゲーム開始時の処理
private void Start()
{
if (startCalled) return; // Startが二重に呼ばれないように
startCalled = true;
Debug.WriteLine("[Start] ゲーム開始処理を実行しました");
MessageBox.Show("Start: ゲーム開始処理が実行されました", "Unityライフサイクル", MessageBoxButtons.OK, MessageBoxIcon.Information);
updateTimer.Start(); // Updateの開始
fixedUpdateTimer.Start(); // FixedUpdateの開始
}
// UnityのUpdate相当: 毎フレーム実行される処理
private void Update(object sender, EventArgs e)
{
Debug.WriteLine($"[Update] フレーム更新: {stopwatch.ElapsedMilliseconds}ミリ秒経過");
LateUpdate(); // Updateの後にLateUpdateを呼び出す
}
// UnityのFixedUpdate相当: 一定間隔で実行される処理(物理計算向け)
private void FixedUpdate(object sender, EventArgs e)
{
Debug.WriteLine($"[FixedUpdate] 物理演算処理: {stopwatch.ElapsedMilliseconds}ミリ秒経過");
}
// UnityのLateUpdate相当: Update後に実行される処理
private void LateUpdate()
{
Debug.WriteLine($"[LateUpdate] 後処理: {stopwatch.ElapsedMilliseconds}ミリ秒経過");
}
// フォームが閉じられるときに実行(UnityのOnDestroy相当)
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
updateTimer?.Stop(); // Updateのタイマーを停止
fixedUpdateTimer?.Stop(); // FixedUpdateのタイマーを停止
Debug.WriteLine("[OnDestroy] アプリケーション終了処理を実行しました");
}
}
}
[Awake] 初期化処理を実行しました
'UnityLifecycleSimulation.exe' (CoreCLR: clrhost): 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\9.0.2\System.ComponentModel.TypeConverter.dll' が読み込まれました。シンボルの読み込みをスキップしました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
[Start] ゲーム開始処理を実行しました
[Update] フレーム更新: 3568ミリ秒経過
[LateUpdate] 後処理: 3574ミリ秒経過
[Update] フレーム更新: 3607ミリ秒経過
[LateUpdate] 後処理: 3610ミリ秒経過
[FixedUpdate] 物理演算処理: 3623ミリ秒経過
[Update] フレーム更新: 3632ミリ秒経過
[LateUpdate] 後処理: 3634ミリ秒経過
[Update] フレーム更新: 3647ミリ秒経過
[LateUpdate] 後処理: 3650ミリ秒経過
[FixedUpdate] 物理演算処理: 3679ミリ秒経過
[Update] フレーム更新: 3681ミリ秒経過
[LateUpdate] 後処理: 3684ミリ秒経過
[Update] フレーム更新: 3701ミリ秒経過
[LateUpdate] 後処理: 3704ミリ秒経過
[Update] フレーム更新: 3737ミリ秒経過
[LateUpdate] 後処理: 3741ミリ秒経過
[FixedUpdate] 物理演算処理: 3753ミリ秒経過
[Update] フレーム更新: 3770ミリ秒経過
[LateUpdate] 後処理: 3773ミリ秒経過
[Update] フレーム更新: 3801ミリ秒経過
[LateUpdate] 後処理: 3804ミリ秒経過
[FixedUpdate] 物理演算処理: 3817ミリ秒経過
[Update] フレーム更新: 3820ミリ秒経過
[LateUpdate] 後処理: 3822ミリ秒経過
[Update] フレーム更新: 3855ミリ秒経過
[LateUpdate] 後処理: 3857ミリ秒経過
[FixedUpdate] 物理演算処理: 3886ミリ秒経過
[Update] フレーム更新: 3889ミリ秒経過
[LateUpdate] 後処理: 3892ミリ秒経過
[Update] フレーム更新: 3918ミリ秒経過
[LateUpdate] 後処理: 3920ミリ秒経過
[Update] フレーム更新: 3930ミリ秒経過
[LateUpdate] 後処理: 3933ミリ秒経過
[FixedUpdate] 物理演算処理: 3946ミリ秒経過
[Update] フレーム更新: 3948ミリ秒経過
[LateUpdate] 後処理: 3950ミリ秒経過
[Update] フレーム更新: 3977ミリ秒経過
[LateUpdate] 後処理: 3979ミリ秒経過
[FixedUpdate] 物理演算処理: 4008ミリ秒経過
[Update] フレーム更新: 4010ミリ秒経過
[LateUpdate] 後処理: 4012ミリ秒経過
[Update] フレーム更新: 4040ミリ秒経過
[LateUpdate] 後処理: 4042ミリ秒経過
[FixedUpdate] 物理演算処理: 4072ミリ秒経過
[Update] フレーム更新: 4074ミリ秒経過
[LateUpdate] 後処理: 4076ミリ秒経過
[Update] フレーム更新: 4104ミリ秒経過
[LateUpdate] 後処理: 4106ミリ秒経過
[FixedUpdate] 物理演算処理: 4130ミリ秒経過
[Update] フレーム更新: 4132ミリ秒経過
[LateUpdate] 後処理: 4134ミリ秒経過
[Update] フレーム更新: 4147ミリ秒経過
[LateUpdate] 後処理: 4149ミリ秒経過
[Update] フレーム更新: 4183ミリ秒経過
[LateUpdate] 後処理: 4185ミリ秒経過
[FixedUpdate] 物理演算処理: 4199ミリ秒経過
[Update] フレーム更新: 4202ミリ秒経過
[LateUpdate] 後処理: 4203ミリ秒経過
[Update] フレーム更新: 4233ミリ秒経過
[LateUpdate] 後処理: 4236ミリ秒経過
[FixedUpdate] 物理演算処理: 4259ミリ秒経過
[Update] フレーム更新: 4261ミリ秒経過
[LateUpdate] 後処理: 4264ミリ秒経過
[Update] フレーム更新: 4277ミリ秒経過
[LateUpdate] 後処理: 4279ミリ秒経過
[Update] フレーム更新: 4307ミリ秒経過
[LateUpdate] 後処理: 4309ミリ秒経過
[FixedUpdate] 物理演算処理: 4324ミリ秒経過
[Update] フレーム更新: 4326ミリ秒経過
[LateUpdate] 後処理: 4328ミリ秒経過
[Update] フレーム更新: 4350ミリ秒経過
[LateUpdate] 後処理: 4352ミリ秒経過
[FixedUpdate] 物理演算処理: 4384ミリ秒経過
[Update] フレーム更新: 4387ミリ秒経過
[LateUpdate] 後処理: 4388ミリ秒経過
[Update] フレーム更新: 4400ミリ秒経過
[LateUpdate] 後処理: 4403ミリ秒経過
[Update] フレーム更新: 4431ミリ秒経過
[LateUpdate] 後処理: 4434ミリ秒経過
[FixedUpdate] 物理演算処理: 4447ミリ秒経過
[Update] フレーム更新: 4450ミリ秒経過
[LateUpdate] 後処理: 4452ミリ秒経過
[Update] フレーム更新: 4482ミリ秒経過
[LateUpdate] 後処理: 4484ミリ秒経過
[OnDestroy] アプリケーション終了処理を実行しました
プログラム '[22860] UnityLifecycleSimulation.exe' はコード 0 (0x0) で終了しました。
5. まとめ
✅ WinFormsのイベントとUnityのライフサイクルの関係を理解
✅ フレーム更新 (Update
) と物理演算 (FixedUpdate
) の違いを体験
✅ フォームのイベント登録方法を学習
このチュートリアルを通じて、Unityのオブジェクトの動作原理を Windowsアプリ開発者の視点から 学べました。
この知識を活かして、実際のUnity開発 に挑戦してみましょう!🚀
ディスカッション
コメント一覧
まだ、コメントがありません