3Dっぽい縦シューティングゲームをつくります。プログラミング講座 第21回【シューティングゲーム作成(1)/JavaScript】を参考にしました。これはJavaScriptでつくられていますが、ここではC#を使います。またそのまま真似してもおもしろくないので3Dっぽい要素を取り入れます。
3DなのでOpenTKを使います。
基本的な仕様ですが、ザコ敵を倒してある程度進むとボス敵が出現します。また敵弾に当たってもすぐ死ぬわけではなくライフが低下していき、ゼロになったらゲームオーバーとします。
ザコ敵は2種類。素材は第23回【シューティング制作.3】 | プログラミング講座@youtubeと第26回【シューティング制作.6】 | プログラミング講座@youtubeにあったものを拝借しました。
画像は[コチラ]に置いておきますので、勉強に使う方がいたらダウンロードしてお使いください。
とのことなので、ありがたく利用させていただきましょう。もとの素材は「やさぐれひよこ素材」置き場にあります。
【ひよこ規約】を一部抜粋
■ 色変更・改変・再配布・使用用途は自由ぴよ。
色を変えたり、改造したり、タッチを似せて作った素材は、「自作素材」と宣言して配布してもらって大丈夫ぴよ。
とのことです。
では最初に基本的な部分だけ示します。タイマーをセットしてTickイベントが発生したら自作メソッドUpdate()を実行し、そのあとGLControlEx.Refresh()メソッドで再描画をしています。
それから視体積の設定はSetProjection()メソッド内で、透視射影、zNearを0.01fに、zFarを40.0fにしています。
1 2 3 4 5 6 7 8 |
public class GLControlEx: OpenTK.GLControl { public GLControlEx() { // ないとForm1でKeyDown、KeyUpイベントが捕捉できない SetStyle(ControlStyles.Selectable, false); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); Timer.Interval = 30; Timer.Tick += Timer_Tick; Timer.Start(); } Timer Timer = new Timer(); private void Timer_Tick(object sender, EventArgs e) { Update(); glControl.Refresh(); } /// <summary> /// コントロールがロードされたら初期化をする /// </summary> private void glControlEx1_Load(object sender, EventArgs e) { GL.ClearColor(glControl.BackColor); // Projection の設定 SetProjection(); // デプスバッファの使用 GL.Enable(EnableCap.DepthTest); Lighting(); glControl.Refresh(); } /// <summary> /// Projection の設定 /// </summary> private void SetProjection() { // ビューポートの設定 GL.Viewport(0, 0, glControl.Width, glControl.Height); // 視体積の設定 GL.MatrixMode(MatrixMode.Projection); Matrix4 proj = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, (float)glControl.Size.Width / (float)glControl.Size.Height, 0.01f, 40.0f); GL.LoadMatrix(ref proj); // MatrixMode を元に戻す GL.MatrixMode(MatrixMode.Modelview); } private void glControl_Resize(object sender, EventArgs e) { SetProjection(); glControl.Refresh(); } void Lighting() { GL.Enable(EnableCap.Normalize); // 光源の使用 GL.Enable(EnableCap.Lighting); // ライト 0 の設定と使用 float[] position = new float[] { 1.0f, 1.0f, 1.0f, 0.0f }; GL.Light(LightName.Light0, LightParameter.Position, position); float[] ambientColor = new float[] { 0.1f, 0.1f, 0.1f, 1.0f }; GL.Light(LightName.Light0, LightParameter.Ambient, ambientColor); GL.Light(LightName.Light0, LightParameter.Diffuse, Color.White); GL.Enable(EnableCap.Light0); } void InitGame() { // 後述 } new void Update() { // 後述 } /// <summary> /// 実際に描画する /// </summary> private void glControlEx1_Paint(object sender, PaintEventArgs e) { GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); // 後述 glControl.SwapBuffers(); } protected override void OnKeyUp(KeyEventArgs e) { // 後述 } protected override void OnKeyDown(KeyEventArgs e) { // 後述 } } |
次に視点の位置ですが、X座標は0,Y座標は最初は-6f、以降はUpdateメソッドが実行されるたびに0.2fずつ前に進行させます。Z座標は5fです。またターゲットはX座標は0,Y座標は最初は7f(以降はUpdateメソッドが実行されるたびに0.2fずつ前に移動)、Z座標は0です。XY平面を少し上から眺め下ろした形になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
public partial class Form1 : Form { static public float BaseY = 0; float InitEyeY = -6f; float InitEyeZ = 5f; float InitTargetY = 7f; // 他のクラスからForm1.XXXでアクセスできるようにstaticにしている static public float Speed = 0.2f; float _eyeX = 0; float EyeX { set{ _eyeX = value; } get{ return _eyeX; } } float _eyeY = 0; float EyeY { set { _eyeY = value;} get { return _eyeY; } } float _targetY = 0; float TargetY { set { _targetY = value; } get { return _targetY; } } float _targetZ = 0; float TargetZ { set { _targetZ = value;} get { return _targetZ; } } void SetSight() { // 視界の設定 Vector3 eye = new Vector3(0, InitEyeY + BaseY, InitEyeZ); Vector3 target = new Vector3(0, InitTargetY + BaseY, 0); Vector3 up = Vector3.UnitY; Matrix4 look = Matrix4.LookAt(eye, target, up); GL.LoadMatrix(ref look); } new void Update() { BaseY += Speed; } void InitGame() { BaseY = 0; } /// <summary> /// 実際に描画する /// </summary> private void glControlEx1_Paint(object sender, PaintEventArgs e) { GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); Lighting(); SetSight(); DrawField(); glControl.SwapBuffers(); } } |
それっぽく見せるために自作メソッドDrawField()で縦横に直線を描画しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public partial class Form1 : Form { void DrawField() { GL.Material(MaterialFace.Front, MaterialParameter.Ambient, Color.Green); GL.Material(MaterialFace.Front, MaterialParameter.Diffuse, Color.Green); // 方眼を描画 for(int i = -50; i < 50; i++) { GL.Normal3(Vector3.UnitZ); GL.Begin(BeginMode.Lines); { GL.Vertex3(2 * i, BaseY + 200, -0.1); GL.Vertex3(2 * i, BaseY - 1, -0.1); } GL.End(); } GL.Material(MaterialFace.Front, MaterialParameter.Ambient, Color.Green); GL.Material(MaterialFace.Front, MaterialParameter.Diffuse, Color.Green); int first = (int)BaseY / 4 * 4; for(int i = 0; i < 100; i++) { GL.Normal3(Vector3.UnitZ); GL.Begin(BeginMode.Lines); { GL.Vertex3(50, first + 2 * i, -0.1); GL.Vertex3(-50, first + 2 * i, -0.1); } GL.End(); } } } |
わからない部分があり、質問失礼しますm(_ _)m
著者さんと同様にFormのGLcontrolで、タイマーで更新しつつキー処理を受け付けるようにしたいのですが、タイマーは起動できたもののキー処理を受け付けてくれません。
そこで、記事に書かれていた↓のコードを挿入すればよいのかと思いましたが、classの場所が見当たらず、挿入場所がわかりません。どこから入れれば良いでしょうか…?
—————————————————————
public class GLControlEx: OpenTK.GLControl
{
public GLControlEx()
{
// ないとForm1でKeyDown、KeyUpイベントが捕捉できない
SetStyle(ControlStyles.Selectable, false);
}
}
—————————————————————
また、
protected override void OnKeyDown(KeyEventArgs e){}
や
protected override void OnKeyUp(KeyEventArgs e){}
は、Formのプロパティのイベントから生成したものでよろしいでしょうか?
プロパティのイベントから(KeyDown)(KeyUp)を生成すると
private void Form1_KeyDown(object sender, KeyEventArgs e){}
となり、著者さんのと若干異なる形になります。
よろしくお願いいたします。
質問していただきありがとうございます。さっそく質問に答えます。
>著者さんと同様にFormのGLcontrolで、タイマーで更新しつつキー処理を受け付けるようにしたいのですが、タイマーは起動できたもののキー処理を受け付けてくれません。
そのままOpenTK.GLControlを貼り付けてしまうとフォーカスをもっていかれるためForm1でKeyDown、KeyUpイベントが捕捉できません。そこでOpenTK.GLControlを継承して新しいクラスを作成しています。「ないとForm1でKeyDown、KeyUpイベントが捕捉できない」と書かれている部分を記述することでフォーカスをもっていかれないのでForm1でKeyDown、KeyUpイベントが捕捉できるようになります。
>classの場所が見当たらず、挿入場所がわかりません。どこから入れれば良いでしょうか?
クラスはOpenTK.GLControlを継承して自分で作成してください。クラスの名前はなんでもかまいません。よくわからない場合は以下をそのままコピペしましょう。
そうするとツールボックスにGLControlExが表示されるので、これをフォームにドラッグアンドドロップすればできるはずです。
>また、protected override void OnKeyDown(KeyEventArgs e){}やprotected override void OnKeyUp(KeyEventArgs e){}は、Formのプロパティのイベントから生成したものでよろしいでしょうか?
いいえ違います。これらはFormのプロパティのイベントから生成したものでありません。これは基底クラスのvoid OnKeyDown(KeyEventArgs e){}やvoid OnKeyUp(KeyEventArgs e){}を「オーバーライド」したものです。この部分はそのままコピペしていただければ動いてくれるはずです。
回答ありがとうございます!
おかげさまで、無事キー処理を追加することができました。
ずっとGLcontrolでタイマー処理+キー処理を実装したいと考えていたものの、方法がわからず困っていたのでとても助かりました。
質問に回答いただき、ありがとうございました!
いつもお世話になっております。タイトル画面と操作方法の画面を追加したいと思っており、新しくフォームを作成すればいいと思ったのですが、上手くいきませんでした。
どのようにすれば上手くいくかを参考までに教えてほしいです。
お手数をおかけしますがよろしくお願いします。
>タイトル画面と操作方法の画面を追加したいと思っており、新しくフォームを作成すればいいと思ったのですが、上手くいきませんでした。
「新しくフォームを作成すれば」ということはメニューをクリックすると別のフォームが現れて、そこに操作方法を表示させるということですか?
それなら難しくないと思うのですが。
それとも元から存在するフォームにタイトルと操作方法を表示させるという意味ですか?
お世話になっております。
今まで作成したFrom1の他に新しいFromでタイトル画面と操作画面をそれぞれ作成しそれをタイトル→操作方法→ゲーム画面の順で呼びだしたいのですが、Fromの呼び出し順序のところが上手くいきませんでした。(From3,2,1の順で呼び出したい。)
こちらの勉強不足かもしれませんが、アドバイスをお願いします。
このようなものでいいのでしょうか?
お世話になっております。はいその通りです。
質問に答える記事をアップしました。
https://lets-csharp.com/display-other-than-form1-first/
色々と丁寧に対応して頂きありがとうございます。とても助かります。
こちらのシューティングゲームを参考にさせていただきます。
完成しているものの動きなどを確認しながら作成を進めたいと思っております。
もしよければ、こちらの完成したソースコードなどを頂くことは可能でしょうか?
お手数をおかけしますがよろしくお願いします。
こちらのサイトにあるシューティングゲームを参考にして今、C#でシューティングゲームの作成をさせていただいています。
サイトに記載してあるコードをコピーして動かしてみたのですが、エラーになってしまいどこが違うのかを確認したいので、良ければ完成したソースコード等のファイルを頂くことは可能でしょうか?
大変厚かましいお願いだとは思いますが、どうぞよろしくお願いいたします。
ここからダウンロードできます。
https://lets-csharp.com/zip/lengthwise-shooting-game.zip