タワーディフェンスが作りたい:その2 〜メニューからバリケードを配置してみる〜

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

はい、前回に引き続きタワーディフェンスの今回は配置するところ。
前回のと合わせて配置するところのWebPlayer版です。
軽くなるようにオブジェクトなどはすべてとっぱらっています。

こんな感じ
Pキーまたはマウスホイールクリックでメニューが出るのでそこから建設->バリケードで配置できます。

shaderまわりがよくわからず演出がしょぼい・・・orz

やっていることは

位置指定

  • プレイヤーから一定範囲内にしか移動させない。
  • 何かにぶつかっている場合はおけない(赤色)
  • おけるときはとりあえず青色
  • ホイールクリックまたはPキーで向き指定へ

向き指定

位置が決め終わったら向きを決めます

  • マウスの位置から向く方向を指定
  • 何かにぶつかっている場合はおけない(赤色)
  • おけるときはとりあえず青色
  • 青色のときにホイールクリックまたはPキーで建て始める

です。

メニューからBuildPositionが呼ばれコルーチンで配置を開始します。
とくに難しいことはなくカメラだけ上からの見下ろしになるようにTweenで処理しています。

1
2
3
4
5
6
7
8
9
10
public void BuildPosition(string prefabPath)
  {
      playerMode       = PlayerMode.BUILD;
      iTween.MoveTo(playerCameraParentTran.gameObject, iTween.Hash("z", 1.2f, "easeType", "easeInOutQuad", "time", 1, "islocal", true));
      iTween.RotateTo(playerCameraParentTran.gameObject, iTween.Hash("x", -56, "easeType", "easeInOutQuad", "time", 1, "islocal", true));

      StartCoroutine(BuildTrapPosition(prefabPath));

  }
  

ちなみにカメラはPlayerの下にカメラ操作用のPlayerCameraParentがあり、その下にカメラがあります。
iTweenではこのカメラの親の方を動かしています。
PlayerCameraParentの中心点はキャラクターの中心位置にあるのでそこから回転させれば簡単に距離感を保ったまま真上にカメラを持っていけます。

こちらが位置を選ぶときのコルーチンです
位置を選んでいるときはトラップのColliderを一度isTriggerにチェックすることでぶつかっても移動しないようにしています。
ぶつかっているときにトラップの色を変えているのはトラップの方のクラスでやっています。
そして確定されたらBuildTrapRotationのコルーチンで今度は回転させます

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
private IEnumerator BuildTrapPosition(string prefabPath)
{
  yield return new WaitForSeconds(0.5f);
  GameObject obj = Instantiate(Resources.Load(prefabPath)) as GameObject;
  buildObjTran = obj.transform;
  buildObjTran.parent = trapTran;
  buildObjTran.localScale = Vector3.one;
  buildObjTran.localPosition = transform.position - transform.TransformDirection(Vector3.forward);
  buildObjTran.rotation = transform.rotation;
  buildObjTran.GetComponent<BoxCollider>().isTrigger = true;
  buildObjTran.GetComponent<TrapBase>().IsMove = true;
  buildProjector.SetActive(true);

  bool flg = true;
  while(flg && buildObjTran != null)
  {
      if(Input.GetMouseButtonDown(0))
      {
          flg = false;
      }
      else
      {
          Vector3 position = playerCameraTran.GetComponent<Camera>().ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y,Camera.main.transform.position.y));

          float dist = Vector2.Distance(new Vector2(transform.position.x, transform.position.z), new Vector2(position.x, position.z));
          if(dist > moveMax) dist = moveMax;
          float dx = position.x - transform.position.x;
          float dy = position.z - transform.position.z;
          float rad = Mathf.Atan2(dy, dx);
          position.x = Mathf.Cos(rad) * dist;
          position.z = Mathf.Sin(rad) * dist;
          buildObjTran.position = transform.position + position;

          yield return new WaitForSeconds(0.01f);
      }
  }

  if(buildObjTran != null)
  {
      StartCoroutine(BuildTrapRotation());
  }
}

こちらが回転のコルーチンです。
途中でキャンセルされた場合や、配置できないまま確定された場合はCancelBuildが呼ばれ配置を終了します。

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
private IEnumerator BuildTrapRotation()
{
  yield return new WaitForSeconds(0.1f);
  bool flg = true;
  while(flg && buildObjTran != null)
  {
      if(Input.GetMouseButtonDown(0))
      {
          flg = false;
      }
      else
      {
          Vector3 mousePos = new Vector3(Input.mousePosition.x,Input.mousePosition.y,Camera.main.transform.position.y);
          Vector3 worldPos = Camera.main.ScreenToWorldPoint(mousePos);
          iTween.LookUpdate(buildObjTran.gameObject,iTween.Hash("looktarget",worldPos,"time",5,"axis","y"));
          playerMode = PlayerMode.ACTION_WAIT;
          yield return new WaitForSeconds(0.01f);
      }
  }

  if(buildObjTran != null)
  {
      TrapBase trapBase = buildObjTran.GetComponent<TrapBase>();
      if(trapBase.IsBuild)
      {
          buildObjTran.GetComponent<BoxCollider>().isTrigger = false;
          trapBase.IsMove = false;

          GameObject gauge = Instantiate(Resources.Load(CommonConst.PREFAB_UI_GAUGE)) as GameObject;
          gauge.transform.parent = ui3DTran;
          gauge.transform.localScale = Vector3.one;
          trapBase.Build(gauge.transform);

          iTween.MoveTo(playerCameraParentTran.gameObject, iTween.Hash("z", 0, "easeType", "easeInOutQuad", "time", 1, "islocal", true));
          iTween.RotateTo(playerCameraParentTran.gameObject, iTween.Hash("x", 0, "easeType", "easeInOutQuad", "time", 1, "islocal", true));
          buildProjector.SetActive(false);
          buildObjTran = null;

          trapBase.BuildEnd(2f, BuildEnd);
      }
      else
      {
          StartCoroutine(CancelBuild());
      }
  }
}

public void BuildEnd()
{
  playerMode = PlayerMode.NORMAL;
}

private IEnumerator CancelBuild()
{
  if(buildObjTran != null) Destroy (buildObjTran.gameObject);
  iTween.MoveTo(playerCameraParentTran.gameObject, iTween.Hash("z", 0, "easeType", "easeInOutQuad", "time", 1, "islocal", true));
  iTween.RotateTo(playerCameraParentTran.gameObject, iTween.Hash("x", 0, "easeType", "easeInOutQuad", "time", 1, "islocal", true));
  buildProjector.SetActive(false);
  buildObjTran = null;
  yield return new WaitForSeconds(1f);
  playerMode = PlayerMode.NORMAL;
}

public void MenuEnd()
{
  playerMode = PlayerMode.NORMAL;
}

こんな感じです
あ、今回は説明しませんが、トラップの色を変えるところの注意点として、
OnTriggerStayでぶつかっているときという判定をとるのですが、ぶつかっていない場合というのは
UpdateでやるのではなくFixedUpdateのほうでやります。
これはUnityが
FixedUpdate
OnTrigger系
OnCollision系
Updateの順番で呼ぶからです。

イベント関数の実行順

なのでFixedUpdateでflgをtrueにし、
OnTriggerStayでぶつかっていたらflgをfalseにし
Updateでflgによってオブジェクトの色を変えるという処理をしています。
トラップ周りの処理はまた今度^^