トーフメモ

主にゲーム制作

【Unity】エディタ拡張でゲームを作る

f:id:tofgame:20190428235628j:plain

エディタ上で動くゲームをつくりました。かんたんなシューティングです。
ゲーム制作に疲れたときに遊べるのでおすすめです(?)
youtu.be

制作のポイント

Event.current

OnGUIがどのイベントで呼ばれているか取得できる&キー入力などを検知できる

EditorGUI.DrawPreviewTexture

Rectクラスを使って位置が指定できるので、テクスチャを動かせる

Repaint()

Repaintイベントを呼び出せるので、Update関数内で使うと作ったゲームがエディタで動くようになる

動かし方

以下のソースコード6つをEditorフォルダに入れて、Resourcesフォルダに画像ファイルを二つ入れます。
imagePath1、imagePath2の宣言部分に画像の名前を同じものを入れてください。

ソースコード

EditorGame.cs

using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using EditorShootingGame;

public class EditorGame : EditorWindow {
    private static bool gameStarted;
    private static bool gameEnded;
    private static bool changeRoutineFlag;

    private static Player player;
    private static EnemyGenerator enemyGenerator;

    private static List<Bullet> bullets = new List<Bullet>();
    private static List<Enemy> enemies = new List<Enemy>();

    private static string imagePath1 = "Assets/Resources/Rect.png";
    private static string imagePath2 = "Assets/Resources/Rect2.png";

    private static Texture2D image1;
    private static Texture2D image2;

    public static Event _event;

    private static int Score; //スコア

    [MenuItem("EditorGame/Shooting")]
    public static void Open () {
        var window = CreateInstance<EditorGame>();
        window.Show();

        Initialize();
    }

    /// <summary>
    /// 初期化
    /// </summary>
    private static void Initialize () {
        gameStarted = false;
        gameEnded = false;
        changeRoutineFlag = false;

        player = new Player(image1,Vector2.zero);
        enemyGenerator = new EnemyGenerator();

        bullets = new List<Bullet>();
        enemies = new List<Enemy>();

        image1 = AssetDatabase.LoadAssetAtPath<Texture2D>(imagePath1);
        image2 = AssetDatabase.LoadAssetAtPath<Texture2D>(imagePath2);

        Score = 0;
    }

    private void OnGUI () {
        ShowBackground();

        _event = Event.current;

        if(!gameStarted) {
            if(GUILayout.Button("START",GUILayout.Width(position.width - 5))) {
                gameStarted = true;
                player.Pos = new Vector2(position.width / 2,position.height / 2);
            }
        } else {
            if(!gameEnded) {
                //プレイヤーがないならリターン
                if(player == null) return;

                LogicPlayer();
                LogicEnemy();
                LogicBullet();

                ShowPlayer();
                ShowEnemy();
                ShowBullet();

                ShowScore(20,20,TextAnchor.MiddleCenter);

                CheckCollideEnemyToBullet();
                CheckCollidePlayerToEnemy();
            } else {
                //イベントタイプのチェック
                if(!IsRunEventType(EventType.Layout)) {
                    return;
                }

                if(GUILayout.Button("RESTART",GUILayout.Width(position.width - 5))) {
                    Initialize();
                }

                ShowScore(position.y / 2,(int)position.size.x / 10,TextAnchor.MiddleCenter);
            }
        }
    }

    private void Update () {
        //再描画
        Repaint();
    }

    //========================================================================
    /// <summary>
    /// プレイヤー処理
    /// </summary>
    private void LogicPlayer () {
        player.Move(_event);
        player.CheckEdge(new Vector2(position.width,position.height));
    }

    //========================================================================
    /// <summary>
    /// 敵処理
    /// </summary>
    private void LogicEnemy () {
        Enemy tmpEnemy = enemyGenerator.GenerateEnemy(image1,new Vector2(position.width / 2 + Random.Range(-position.width / 2,position.width),0),Score);
        if(!(tmpEnemy == null)) {
            enemies.Add(tmpEnemy);
        }

        for(int i = enemies.Count - 1;i >= 0;i--) {
            enemies[i].Move();
        }

        for(int i = enemies.Count - 1;i >= 0;i--) {
            if(enemies[i].CheckUnderEdge(new Vector2(position.width,position.height))) {
                enemies.RemoveAt(i);
            }
        }
    }

    //========================================================================
    /// <summary>
    /// 弾処理
    /// </summary>
    private void LogicBullet () {
        Bullet tmpBullet = player.GenerateBullet(image1);
        if(!(tmpBullet == null)) {
            bullets.Add(tmpBullet);
        }

        for(int i = bullets.Count - 1;i >= 0;i--) {
            bullets[i].Move();
            if(bullets[i].CheckEdge(new Vector2(position.width,position.height))) {
                bullets.RemoveAt(i);
            }
        }
    }

    //========================================================================
    /// <summary>
    /// プレイヤー描画
    /// </summary>
    private void ShowPlayer () {
        EditorGUI.DrawPreviewTexture(new Rect(player.Pos,player.Size),player.Image);
    }

    //========================================================================
    /// <summary>
    /// 敵描画
    /// </summary>
    private void ShowEnemy () {
        foreach(Enemy e in enemies) {
            EditorGUI.DrawPreviewTexture(new Rect(e.Pos,e.Size),e.Image);
        }
    }

    //========================================================================
    /// <summary>
    /// 弾描画
    /// </summary>
    private void ShowBullet () {
        foreach(Bullet b in bullets) {
            EditorGUI.DrawPreviewTexture(new Rect(b.Pos,b.Size),b.Image);
        }
    }

    //========================================================================
    /// <summary>
    /// 背景描画
    /// </summary>
    private void ShowBackground () {
        EditorGUI.DrawPreviewTexture(new Rect(0,0,position.width,position.height),image2);
    }

    //========================================================================
    /// <summary>
    /// スコア描画
    /// </summary>
    private void ShowScore (float yPos,int size,TextAnchor ta) {
        GUILayout.Space(yPos);
        GUIStyle myStyle = new GUIStyle();
        myStyle.normal.textColor = Color.white;
        myStyle.fontSize = size;
        myStyle.alignment = ta;
        EditorGUILayout.LabelField("スコア:" + Score,myStyle);
    }

    //========================================================================
    /// <summary>
    /// 敵と弾の衝突判定
    /// </summary>
    private void CheckCollideEnemyToBullet () {
        for(int i = enemies.Count - 1;i >= 0;i--) {
            for(int j = bullets.Count - 1;j >= 0;j--) {
                if(enemies[i].IsCollide(bullets[j])) {
                    enemies.RemoveAt(i);
                    bullets.RemoveAt(j);
                    Score++;
                    break;
                }
            }
        }
    }

    //========================================================================
    /// <summary>
    /// プレイヤーと敵の衝突判定
    /// </summary>
    private void CheckCollidePlayerToEnemy () {
        for(int i = enemies.Count - 1;i >= 0;i--) {
            if(player.IsCollide(enemies[i])) {
                player = null;
                gameEnded = true;
                changeRoutineFlag = true;
                break;
            }
        }
    }

    //========================================================================
    /// <summary>
    /// 特定のイベントが動いているかどうか
    /// </summary>
    private bool IsRunEventType (EventType e) {
        if(changeRoutineFlag) {
            if(!(_event.type == EventType.Layout)) {
                return false;
            }

            changeRoutineFlag = false;
        }

        return true;
    }
}

BaseRect.cs

using UnityEngine;

namespace EditorShootingGame {
    public class BaseRect {
        public Texture2D Image;

        public Vector2 Pos;
        public Vector2 Size;

        public BaseRect (Texture2D image,Vector2 size) {
            Image = image;
            Pos = Vector2.zero;
            Size = size;
        }

        public BaseRect (Texture2D image,Vector2 pos,Vector2 size) {
            Image = image;
            Pos = pos;
            Size = size;
        }

        //==================================================================================
        /// <summary>
        /// 端チェック
        /// </summary>
        public bool CheckEdge (Vector2 basePos) {
            bool isEdge = false;

            if(Pos.x >= (basePos.x - Size.x)) {
                Pos.x = basePos.x - Size.x;
                isEdge = true;
            }

            if(Pos.x <= 0) {
                Pos.x = 0;
                isEdge = true;
            }

            if(Pos.y >= (basePos.y - Size.y)) {
                Pos.y = basePos.y - Size.y;
                isEdge = true;
            }

            if(Pos.y <= 0) {
                Pos.y = 0;
                isEdge = true;
            }

            return isEdge;
        }

        //==================================================================================
        /// <summary>
        /// 下端のチェック
        /// </summary>
        public bool CheckUnderEdge (Vector2 basePos) {
            bool isEdge = false;

            if(Pos.y >= (basePos.y - Size.y)) {
                Pos.y = basePos.y - Size.y;
                isEdge = true;
            }

            return isEdge;
        }

        //==================================================================================
        /// <summary>
        /// 他BaseRectとの衝突判定
        /// </summary>
        /// <param name="enemy"></param>
        public bool IsCollide (BaseRect baseRect) {
            if(Vector2.Distance(Pos,baseRect.Pos) < (Size.x + Size.y + baseRect.Size.x + baseRect.Size.y) / 4) {
                return true;
            }

            return false;
        }
    }
}

Player.cs

using UnityEngine;

namespace EditorShootingGame {
    public class Player : BaseRect {
        public float Speed;
        private float firstPosY = 0.9f;

        private int generateBulletTime;
        private const int GENERATE_BULLET_TIME_LENGTH = 100;

        public Player (Texture2D image,Vector2 midPos) : base(image,new Vector2(20,20)) {
            Pos = new Vector2(midPos.x / 2 - Size.x / 2,midPos.y * firstPosY - Size.y);
            Speed = 6;
        }

        //==================================================================================
        /// <summary>
        /// 移動
        /// </summary>
        public void Move (Event e) {
            //右
            if(e.keyCode == KeyCode.RightArrow) {
                Pos += Vector2.right * Speed;
            }

            //左
            if(e.keyCode == KeyCode.LeftArrow) {
                Pos += Vector2.left * Speed;
            }

            //上(上下逆)
            if(e.keyCode == KeyCode.UpArrow) {
                Pos += Vector2.up * Speed * (-1);
            }

            //下(上下逆)
            if(e.keyCode == KeyCode.DownArrow) {
                Pos += Vector2.down * Speed * (-1);
            }
        }

        //==================================================================================
        /// <summary>
        /// 弾の生成
        /// </summary>
        /// <param name="image"></param>
        public Bullet GenerateBullet (Texture2D image) {
            generateBulletTime++;
            if(generateBulletTime >= GENERATE_BULLET_TIME_LENGTH) {
                generateBulletTime = 0;
            }

            if(generateBulletTime > 0) return null;
            return new Bullet(image,Pos + Size / 2);
        }
    }
}

Bullet.cs

using UnityEngine;

namespace EditorShootingGame {
    public class Bullet : BaseRect {
        public float Speed;

        public Bullet (Texture2D image,Vector2 pos) : base(image,pos,new Vector2(10,10)) {
            Pos -= Size / 2;
            Speed = 4;
        }

        public void Move () {
            //上移動(逆)
            Pos += Vector2.up * Speed * (-1);
        }
    }
}

EnemyGenerator.cs

using UnityEngine;

namespace EditorShootingGame {
    public class EnemyGenerator {
        private int generateBulletTime;
        private int generateBulletTimeLength = 120;

        /// <summary>
        /// 敵の生成
        /// </summary>
        public Enemy GenerateEnemy (Texture2D image,Vector2 pos,float speed) {
            generateBulletTime++;
            if(generateBulletTime >= generateBulletTimeLength - speed) {
                generateBulletTime = 0;
            }

            if(generateBulletTime > 0) return null;
            return new Enemy(image,pos);
        }
    }
}

Enemy.cs

using UnityEngine;

namespace EditorShootingGame {
    public class Enemy : BaseRect {
        public float Speed;
        public Enemy (Texture2D image,Vector2 pos):base(image,pos,new Vector2(20,20)) {
            Pos -= Size / 2;
            Speed = 0.3f;
        }

        public void Move () {
            //上移動(逆)
            Pos += Vector2.down * Speed * (-1);
        }
    }
}