タワーディフェンスが作りたい:その3 〜ゲージの表示周り〜

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

はい、引き続きちょこちょこタワーディフェンスをつくっています。
今回はHPのゲージや、ダメージを表示するところをの説明でも・・・。

作ったものとしては

  • 位置が変わっても位置が変わらずに表示される
  • ダメージ、ゲージなどは位置に関係なくて前に描画される
  • 距離が離れると非表示になる

こんな感じのものです。

カメラ構成

ヒエラルキーはこんな感じになっています。

メインのカメラはPlayerの下にあるカメラで、今回の3D空間上のUI表示用に
UI Root(3D)のカメラを使います。
UI Rootの方は2Dのメニュー(最前面)表示用に使います
さらにもう一つカメラがあってそれがMapの下にMap表示用のかめらがあるので
カメラは全部で4つ使っています。

  • 3dUIカメラ:レイヤー 3DUIのみ表示 Depth:0
  • 2dUIカメラ:レイヤー UIのみ表示 Depth:2
  • マップ用カメラ:レイヤー Mapのみ表示 Depth:1
  • Playerカメラ:上記以外のレイヤーを表示 Depth:-1

3dUIカメラはこんなような感じになっています

projectionはPerspective、Clippingでどれくらいはなれたら非表示にするかというのができます。

##早速表示 ゲージのprefabとダメージテキストのprefabをつくります。
ゲージはEnemyのクラスのStartとりあえず作成します。
親はUI Root(3D)のしたです。

private Transform gaugeTran;
protected Gauge gauge;
private int maxHp = 10;
private int hp = 10;
private bool isDead = false;
private Transform target;
void Start () {
GameObject gaugeObj = Instantiate(Resources.Load(CommonConst.PREFAB_UI_GAUGE)) as GameObject;
gaugeObj.transform.parent = BattleCommonFunc.ui3DCameraTran.parent;
gaugeObj.transform.localScale = Vector3.one;
gaugeObj.gameObject.SetActive(false);
gaugeTran = gaugeObj.transform;
gauge = gaugeObj.GetComponent<Gauge>();
}

普通に作成しているだけです。
位置などはUpdateで計算します

ちなみにゲージのクラスはこんな風になっています。

public class Gauge : MonoBehaviour {
public UISprite gaugeSprite;
public UILabel gaugeLabel;
private Transform tran;
private bool isShow = true;
void Awake()
{
tran = transform;
}
// ゲージの表示、非表示きりかえ
public void Show(bool flg)
{
if(isShow != flg)
{
foreach(Transform child in tran)
{
child.gameObject.SetActive(flg);
}
isShow = flg;
}
}
// ゲージだけ表示
public void SetGauge(float amount)
{
Show(true);
gaugeSprite.fillAmount = amount;
gaugeLabel.text = "";
}
// ゲージとテキストを表示
public void SetGauge(float amount, string str)
{
SetGauge(amount);
gaugeLabel.text = str;
}
}

こちらがEnemyクラスのUpdateメソッドです。
死んだときと、HPが最大のときは非表示にしています。
位置を合わせているのはGetMainTo3DUIPosというメソッド。
下の方で説明します。

void Update () {
if(isDead) return;
if(hp != maxHp)
{
gaugeTran.gameObject.SetActive(true);
gauge.SetGauge((float)hp / (float)maxHp, hp.ToString());
}
if(gaugeTran.gameObject.activeSelf)
{
gaugeTran.position = BattleCommonFunc.GetMainTo3DUIPos(transform.position);
gaugeTran.LookAt(BattleCommonFunc.ui3DCameraTran.position);
gaugeTran.SetEulerAnglesY(0);
gaugeTran.AddPositionY(1f);
}
}

で、こちらが要のGetMainTo3DUIPosです。
メインカメラの座標を3dカメラの座標に変換しているところです。

public static Vector3 GetMainTo3DUIPos(Vector3 pos)
{
Vector3 uiPos = mainCamera.GetComponent<Camera>().WorldToScreenPoint(pos);
uiPos = ui3DCamera.GetComponent<Camera>().ScreenToWorldPoint(uiPos);
return uiPos;
}

このメソッドをダメージの表示のところでも使えばOKです。

EnemyClassのあたり判定部分

void OnTriggerEnter(Collider other) {
if(!isDead && other.tag == CommonConst.TAG_PLAYER_ATTACK)
{
GameObject damageText = Instantiate(Resources.Load(CommonConst.PREFAN_UI_DAMAGE_TEXT)) as GameObject;
damageText.transform.parent = BattleCommonFunc.ui3DCameraTran.parent;
damageText.transform.localScale = Vector3.one;
DamageText dmgTxt = damageText.GetComponent<DamageText>();
dmgTxt.SetText("10", tran); // TODO とりあえず固定でダメージ10
StartCoroutine(Hit (10)); // ダメージを食らったアニメーションなど ここでは割愛
}
}

DamageTextに関してはこのクラスのUpdateで位置の計算をしたり、アルファ、移動処理をしています。

public class DamageText : MonoBehaviour {
private Transform tran;
private Transform targetTran;
private float movePos = 0;
private UILabel damageLebel;
public void SetText(string str, Transform targetTran)
{
this.targetTran = targetTran;
damageLebel = GetComponent<UILabel>();
damageLebel.text = str;
tran = transform;
Destroy (gameObject, 1);
}
void Update () {
if(tran != null)
{
movePos += 100f * Time.deltaTime;
tran.position = BattleCommonFunc.GetMainTo3DUIPos(targetTran.position);
tran.AddLocalPositionY(movePos + 100f);
tran.LookAt(BattleCommonFunc.ui3DCameraTran.position);
tran.SetEulerAnglesY(0);
damageLebel.color = new Vector4(damageLebel.color.r, damageLebel.color.g, damageLebel.color.b, damageLebel.color.a - Time.deltaTime);
}
}
}

##最後に 今回はそれほどだいしたことをしていませんが・・・。
実はちょっとあきてきている・・・。

ちなみに現在は下図のような状態までつくりました。

簡単にUIをのせてスライムがクリスタルに向かってぞろぞろスポーンして向かっていくという・・・。
A*も乗りましたがいろいろ修正が必要かもな〜といったところ。
敵のAIとか〜経路探索がみんな同じ道とおっててなんだかつまんない・・・。

ここまでの作業時間はトータルで15時間くらいだろうか。
うん、アセットの力は偉大である。
ここまでではまったのが、クオータニオンとオイラーとかの回転・・・。
あとはUnity4からもってきたProjectorがバグっている件・・・。
Unity5のprojectorをインポートしなおしてshaderを差し替えたらうまく動いた。
これの解決に30分ぐらいついやした・・・。