Unityちゃんを英雄王にしてみた
こんちか!! たくみんです。この記事は、SLP KBIT Advent Calendar 2018の18日目の記事になります。
本題に入りますが、Fateに興味をもちまして、Fate/stay night [Unlimited Blade Works]を視聴しました。
一応最後まで見たんですが、「王の財宝」がかっこよすぎるんですよね。まず、「王の財宝」と書いて、「ゲートオブバビロン」というのが、かっこいいし、迫力も凄いし、もう半端じゃないって思いました。
そして、思いました。
そうだ、Unityでやろう。
「王の財宝」の確認
まず、「王の財宝」を確認して、どのような要素で構成されているのか、確認してみましょう。 確認すると、「王の財宝」は3つの要素で構成されていることがわかります。
- 宝具(剣とか槍とか)
- 宝具を射出するやつ
- 英雄王本人
これをUnityで再現します。 つまり、「Unityちゃんが「王の財宝」を使って、的に対して攻撃する」を再現します。 この記事では、「王の財宝」を生成する部分まで紹介します。
Assetの準備
まずは、「王の財宝」を再現するための、Assetを探します。
1. 宝具
宝具に見えるAssetは、下記のものにしました。これなら、無料で使用できますし、それっぽいですからね。 assetstore.unity.com
2. 宝具を射出するやつ
宝具を射出するやつは、下記のものにしました。AssetStoreには、無料でいいものがなかったので、こちらにしました。 ktk-kumamoto.hatenablog.com
3. 英雄王本人
英雄王は、Unityちゃんにしました。ほとんど初めてのUnityなので、下記のQiitaの記事を参考にしました。
今回作成したObjectの紹介
今回Scene上に生成するObjectは以下です。
- Player(Playerが操作するUnityちゃん)
- target(「王の財宝」の的になるObject)
- aura_ground(宝具を射出するやつ)
- sword(宝具)
- mask_sword(後述)
「王の財宝」を生成するスクリプト
生成するスクリプトは、PlayerにAddしているSwordGeneratorというスクリプトです(くそコードなのは許してください)。とりあえず、Hキーを押すことで、「王の財宝」を生成することにしました。
public class SwordGenerator : MonoBehaviour { GameObject sword; GameObject mask_sword; GameObject sword_circle; GameObject player; GameObject target; // Update is called once per frame private void Start() { sword = (GameObject)Resources.Load("Prefabs/WeaponSword002"); mask_sword = (GameObject)Resources.Load("Prefabs/Cylinder"); sword_circle = (GameObject)Resources.Load("Prefabs/aura_ground"); player = GameObject.Find("Player"); target = GameObject.Find("target"); } void Update () { if (Input.GetKeyDown(KeyCode.H) ) { //---- arua_ground(宝具を射出するやつ)の生成 Vector3 pos = new Vector3(Random.Range(-20.0f, 20.0f) + player.transform.position.x, player.transform.position.y + Random.Range(2.0f, 12.0f), player.transform.position.z); Vector3 pos_circle = pos; Vector3 aim = target.transform.position - pos; Quaternion look = Quaternion.LookRotation(aim); pos_circle = pos_circle + (look * new Vector3(0f, 0f, 1.1f)); Instantiate(sword_circle, pos_circle, look); //---- sword(宝具)とmask_swordの生成 switch(Random.Range(0,11)) { case 0: sword = (GameObject)Resources.Load("Prefabs/WeaponSword000"); break; case 1: sword = (GameObject)Resources.Load("Prefabs/WeaponSword001"); break; case 2: sword = (GameObject)Resources.Load("Prefabs/WeaponSword002"); break; case 3: sword = (GameObject)Resources.Load("Prefabs/WeaponSword003"); break; case 4: sword = (GameObject)Resources.Load("Prefabs/WeaponSword004"); break; case 5: sword = (GameObject)Resources.Load("Prefabs/WeaponSword005"); break; case 6: sword = (GameObject)Resources.Load("Prefabs/WeaponSword006"); break; case 7: sword = (GameObject)Resources.Load("Prefabs/WeaponSword007"); break; case 8: sword = (GameObject)Resources.Load("Prefabs/WeaponSword008"); break; case 9: sword = (GameObject)Resources.Load("Prefabs/WeaponSword009"); break; case 10: sword = (GameObject)Resources.Load("Prefabs/WeaponSword010"); break; } Vector3 axis_x = new Vector3(1f, 0f, 0f); Quaternion q_x = Quaternion.AngleAxis(90f, axis_x); Vector3 axis_y = new Vector3(0f, 1f, 0f); Quaternion q_y = Quaternion.AngleAxis(90f, axis_y); look = Quaternion.LookRotation(aim, Vector3.up); look = look * q_x * q_y; Instantiate(sword, pos, look); Instantiate(mask_sword, pos, look); } } }
ひとつずつ説明していきます。
aura_ground(宝具を射出するやつ)の生成
aura_ground(宝具を射出するやつ)の生成を行います。 このコードで行っている処理は、以下の通りです。
- playerの座標と、Random.Range()により、aura_groundを生成する座標を決定
- aura_groundの座標とtargetの座標の差と、Quaternion.LookRotation()によりtargetの方向を向くようにする
- aura_groundの座標を、targetがいる方向に1.1fずらす(宝具の先っぽの方で、aura_groundを生成するため)。
- aura_groundの生成
//---- sword(宝具)を射出するやつの生成 //-- 1. Vector3 pos = new Vector3(Random.Range(-20.0f, 20.0f) + player.transform.position.x, player.transform.position.y + Random.Range(2.0f, 12.0f), player.transform.position.z); Vector3 pos_circle = pos; //-- 2. Vector3 aim = target.transform.position - pos_circle; Quaternion look = Quaternion.LookRotation(aim); //-- 3. pos_circle = pos_circle + (look * new Vector3(0f, 0f, 1.1f)); //-- 4. Instantiate(sword_circle, pos_circle, look);
1. aura_groundを生成する座標を決定
aura_groundを生成する座標を決定します。Unityちゃんの周りに生成してほしいため、Unityちゃんの座標とRandom.Range()を使用します。これにより、Unityちゃんの座標が中心として、一定の範囲内にarua_groundが生成されるようになります。また、今回はZ座標は、Unityちゃんの座標のままにしているので、aura_groundは、XY平面上に生成されます。 イメージとしては、下図のような感じです。赤丸がUnityちゃんだとすると、赤四角の範囲内に、aura_groundが生成されます。
2. targetの方向を向くようにする
targetの方向を向かせるために、以下のサイトを参考にしました。
3. targetの方向に座標をずらす
Vector3型のベクトルにQuaternionを掛け合わせることで、ベクトルを回転させることができます。これにより、「現在の座標」に「target方向に向いた大きさ1.1fのベクトル」を足すことで、座標をずらしています。
4. aura_groundの生成
Instantiate()で生成しているだけです。
sword(宝具)の生成
sword(宝具)の生成を行います。 このコードで行っている処理は、以下の通りです。
- 生成するsword(宝具)をランダムで決める
- aura_groundと、Object自体の向いている方向?が異なっているため、補正
- sword(宝具)とmask_swordの生成
//---- sword(宝具)とmask_swordの生成 //-- 1. switch(Random.Range(0,11)) { case 0: sword = (GameObject)Resources.Load("Prefabs/WeaponSword000"); break; case 1: sword = (GameObject)Resources.Load("Prefabs/WeaponSword001"); break; case 2: sword = (GameObject)Resources.Load("Prefabs/WeaponSword002"); break; case 3: sword = (GameObject)Resources.Load("Prefabs/WeaponSword003"); break; case 4: sword = (GameObject)Resources.Load("Prefabs/WeaponSword004"); break; case 5: sword = (GameObject)Resources.Load("Prefabs/WeaponSword005"); break; case 6: sword = (GameObject)Resources.Load("Prefabs/WeaponSword006"); break; case 7: sword = (GameObject)Resources.Load("Prefabs/WeaponSword007"); break; case 8: sword = (GameObject)Resources.Load("Prefabs/WeaponSword008"); break; case 9: sword = (GameObject)Resources.Load("Prefabs/WeaponSword009"); break; case 10: sword = (GameObject)Resources.Load("Prefabs/WeaponSword010"); break; } //-- 2. Vector3 axis_x = new Vector3(1f, 0f, 0f); Quaternion q_x = Quaternion.AngleAxis(90f, axis_x); Vector3 axis_y = new Vector3(0f, 1f, 0f); Quaternion q_y = Quaternion.AngleAxis(90f, axis_y); look = Quaternion.LookRotation(aim); look = look * q_x * q_y; //-- 3. Instantiate(sword, pos, look); Instantiate(mask_sword, pos, look);
1. ランダムでsword(宝具)を生成
Random.Range()とswitch文により、生成するPrefabを変えています。でもこの書き方はちょっとダサい気がするので、何かいい方法を知っていれば教えてください。
2. 向きの補正
「王の財宝」を再現するためには、sword(宝具)とaura_groundが同じ方向を向いている必要があります。では、以下の2つの画像を見てください。この2つの画像では、2つとも同じ上方向を向かせているつもりなのですが、ObjectのRotationを確認すると、まったく違う方向を向いていることがわかります。そのため、ただ、targetの方向を見るだけでは不十分であり、向きの補正が必要なのです。
4. 宝具の生成
sword(宝具)をInstantiate()するわけですが、このままでは、aura_groundの裏側にもsword(宝具)が表示されてしまいます。「王の財宝」を再現する場合、以下の画像のように、aura_groundの裏側になる部分は、隠れていないとダメなのです。
では、どのようにして、指定の部分のみを非表示にするのか。その答えが以下の記事にありました。 つまり、別のオブジェクトを重ねることで、見かけ上隠すということです。
今回は、swordより少し大きいCylinderをswordに重ねることで、隠すことに成功しました。
スクリプトの実行結果
実行してみると、かなりそれっぽくなっていることがわかりますね。個人的には大満足です。
感想
ほとんど初めてのUnityだったので、Prefabってなに?Assetってなに?からのスタートでした。一番わからないのは、Quaternionですね。定義やリファレンスを見てもワケワカメ状態でした(そもそもベクトルがわからん)。3Dゲームを作るとなると、数学の知識が必要になってくるらしいので勉強しないとまずいかなって感じです。
おわりに
とりあえず「王の財宝」の生成までの記事を書いてみました。次は、射出する話であったり、背景と地形(Unlimited Blade Works再現)の話などを書いていこうかなと思います。ではでは。