タワーディフェンスが作りたい:その1 〜建設メニューをつくろう〜

ayumegu(プログラマー)
よろしくお願いします。

タワーディフェンスつくってみたいな〜

最近2Dのものばかりつくっているので、3Dでなにかゲームを・・・。
タワーディフェンスがつくってみたいんだった。ということで、3Dのタワーディフェンス。
これはさすがに1日〜2日で作れるものではないのでちょこちょこまったり作ろうかと思います。
私の作りではPCでも工夫しないとかくかくしないか心配です・・・w まぁ、とりあえずのものは作れるはず・・・ということで挑戦します。
PC向けでつくります。

参考ゲーム

ちなみに作りたいものはDungeon DefendersというSteamで売っているゲームのようなもの。
タワーを建てるだけでなくプレイヤー自体も戦えるものです!
すごい一時期はまりまして、100時間以上遊んでいました。もう3年くらい前か・・・。

開発環境とアセットなど

環境はMac Unity5.0.1 使用する予定のエディター系スクリプト系アセット

を使う予定です。

A* Pathfinding Project Proにいたってはセールのときに買ったきりサンプルしか試していないのでどうなるかわからないですが・・・ まぁ勉強もかねてw
ま、どこまでできるかわからないですが、とりあえずいつもの飽きるまでやってみようかと思います。

ではさっそくメニューから^^

最初はメニューを作りました

こんなのです

次で記事にする予定のトラップの配置部分も含んでいますが。
このメニューはNGUIを使用していますが、Tweenなどもつかっていないので計算式は他でも使えるはず・・・。

円形にメニューが広がって、建設だけはさらにサブメニューがあるので前のメニューが外に消えて
次のメニューが広がる感じのものです。

メニュー部分のヒエラルキーはこんな感じです。

BaseMenu:デフォルトのメニュー
BuildMenu:トラップ作成用メニュー

その子各メニュー。
Colliderと押したときにBattleMenu.csのMenuClickがよばれるようになっています。
その下に画像や文字がはいっています。
BaseMenuとBuildMenuの子は同じ作りです
子のメニューの数はいくつでもきれいに円形に並べるようにソースをくみました。

BattleMenu.cs

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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class BattleMenu : MonoBehaviour {

  public GameObject baseMenu;                  // デフォルトのメニュー
  public GameObject buildManu;             // トラップ建設のメニュー
  public PlayerController playerController;    // プレイヤーのscript

  private Transform nowMenu;                   // 現在表示対象のメニューのTransform
  private bool menuMoveFlg = false;         // メニューが動いているかどうか
  private float menuRadius = 200;               // メニューの半径
  private float menuNextRadius = 350;           // 次のメニューが表示されるときに前のメニューが外に動くときの半径
  private float moveSpeed = 2000;               // 動くスピード

  void Start () {
      //OpenBaseMenu(baseMenu.transform);
  }

  // PlayerControllerから私の場合はマウスホイールクリックかpキークリックで呼ばれる  
  public void OpenBaseMenu()
  {
      StartCoroutine(OpenMenu (baseMenu.transform));
  }

  // メニューを開く処理 外側にフェードイン 
  private IEnumerator OpenMenu(Transform menu)
  {
      nowMenu = menu;
      menu.gameObject.SetActive(true);
      List<Transform> menuList = new List<Transform>();
      foreach(Transform tran in menu)
      {
          menuList.Add (tran);
      }

      float nowRadius = 0;
      menuMoveFlg = true;
      bool flg = true;
      while(flg)
      {
          if(nowRadius != menuRadius)
          {
              nowRadius += moveSpeed * 0.01f;
              if(nowRadius >= menuRadius)
              {
                  flg = false;
                  nowRadius = menuRadius;
              }
              SetMenuPos(menuList, nowRadius, new Vector4(1,1,1,(float)nowRadius/ (float)menuRadius));
          }
          yield return new WaitForSeconds(0.01f);
      }
      menuMoveFlg = false;
  }

  // 次のメニューが開くので外側にフェード合うとする処理
  private IEnumerator NextMenu(Transform menu)
  {
      menu.gameObject.SetActive(true);
      List<Transform> menuList = new List<Transform>();
      foreach(Transform tran in menu)
      {
          menuList.Add (tran);
      }

      float nowRadius = menuRadius;
      bool flg = true;
      while(flg)
      {
          if(nowRadius != menuNextRadius)
          {
              nowRadius += moveSpeed * 0.01f;
              if(nowRadius >= menuNextRadius)
              {
                  flg = false;
                  nowRadius = menuNextRadius;
              }
              SetMenuPos(menuList, nowRadius, new Vector4(1,1,1,1f - (float)nowRadius/ (float)menuNextRadius));
          }
          yield return new WaitForSeconds(0.01f);
      }
      menu.gameObject.SetActive(false);
  }

  // メニューを閉じるので内側にフェードアウトする処理
  private IEnumerator CloseMenu(Transform menu, bool isEnd)
  {
      menu.gameObject.SetActive(true);
      List<Transform> menuList = new List<Transform>();
      foreach(Transform tran in menu)
      {
          menuList.Add (tran);
      }

      float nowRadius = menuRadius;
      bool flg = true;
      while(flg)
      {
          if(nowRadius != 0)
          {
              nowRadius -= moveSpeed * 0.01f;
              if(nowRadius <= 0)
              {
                  flg = false;
                  nowRadius = 0;
              }

              SetMenuPos(menuList, nowRadius, new Vector4(1,1,1, 1f- (float)(menuRadius - nowRadius)/ (float)menuRadius));
          }
          yield return new WaitForSeconds(0.01f);
      }
      menuMoveFlg = false;
      menu.gameObject.SetActive(false);
      if(isEnd)
          playerController.MenuEnd();
  }

  // メニューを円形に配置する
  private void SetMenuPos(List<Transform> menuList, float nowRadius, Vector4 menuColor)
  {
      float rot = 0;
      float rad = 0;
      Vector3 pos = Vector3.zero;            // サブメニューの位置
      
      for(int i = 0; i < menuList.Count; i++)
      {
          rot = 360 / menuList.Count * i + 90;
          rad = rot * Mathf.Deg2Rad;  // 角度からラジアンに変換
          pos = Vector3.zero;
          pos.x = Mathf.Cos(rad) * nowRadius;
          pos.y = Mathf.Sin(rad) * nowRadius;
          
          menuList[i].localPosition = pos;
          menuList[i].FindChild("Sprite").GetComponent<UISprite>().color = menuColor;
      }
  }

  // PlayerControllerからメニューを閉じるとき、またはメニューが確定されたときによばれる  
  public void CloseMenu()
  {
      StartCoroutine(CloseMenu(nowMenu, true));
  }


  void Update () {
  

  }

  // 各メニューがクリックされたときに呼ばれる
  public void ManuClick(GameObject obj)
  {
      // 建設メニューだけは子のメニューがあるので子のメニューを開く
      if(obj.name == "Build")
      {
          StartCoroutine(NextMenu(baseMenu.transform));
          StartCoroutine(OpenMenu(buildManu.transform));
      }
      // 他はメニューが確定されたときなので好きに処理を書く。  
      else if(obj.name.IndexOf("Build") != -1)
      {
          StartCoroutine(CloseMenu(nowMenu, false));
          playerController.BuildPosition(CommonConst.PREFAB_BARRICADE + "1");
      }
      else
      {
          StartCoroutine(CloseMenu(nowMenu, true));
          Debug.Log (obj.name);
      }
  }
}

はい、こんな感じです。
ちょっとモデルがいっぱいでWebPlayer用にビルドするとサイズが大きくなってしまうので今回は割愛。
次回はバリケード配置部分の処理を・・・そのときにまとめてWebPlayerでビルドします。
今回はここまで〜