プロフィール

はーさん

Author:はーさん
1人創作チームDeadMan'sSunのボスです。
Twitter→@Ha_Shok_ko

TERA HITO:Revolutionを作りました。よろしく頼むぜ、兄弟!
・ふりーむ!のTERAのページ(exe形式公開場所)へ
・UnityRoomのTERAのページ(WebGL版公開場所)へ

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
カウンター
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム
QRコード
QR

私のアイランド


--/--/-- --:-- はーさん

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
2016/03/10 00:55 はーさん

Navmeshの話

Navmeshのことを話します。

Navmeshを使って指定ポイントまで移動するコードを書いたのですが、途中でOffmeshLinkを経由するNavmeshのPathが一度切れて、OffmeshLinkを経由して移動して、Navmeshを焼いたオブジェクトに到達するとPathを再度取得するということが発生しました。
僕の中ではNavmeshのPathはOffmeshLinkも含めて計算するものだと思っていたので
hasPathメソッドでpathの有無を判別して、移動をやめるか、やるかを判定させていたのです。

しかしOffmeshLinkを経由したらPathを失うとなると、別のアプローチが必要になるなぁ、と。

あとNavmeshのremaningDistanceメソッドは移動先が到達できないと分かるや否やインフィニティ↑になってしまうので、普通にVector3のDistanceメソッド使うなり自前の計算式で距離を出したほうが良いかなぁ、と。

うーん。
困りました。Update内で毎フレーム判定させているおかげでコンマうん秒の差で条件を抜けられてアウト。

うーん。

だから自作の経路探索を作れとあれほど!もう!

便利ですけど、うーん・・・Navmesh・・・こんな落とし穴があるとはなぁ・・・

これからは自前の経路探索プログラムを作るべき、か・・・
難しそう・・・

うーん・・・
スポンサーサイト
2015/10/28 01:05 はーさん

【Unity】RTSみたいなゲームオブジェクトの選択方法

お久しぶりです。
はーさんです。
二か月ぶりのUnityの記事です。
最近学校の方とかで忙しいこともあって、記事を書けませんでした。
夏休み時間あっただろ!


今日はUnityでRTSみたいな、マウスをクリックしてからドラッグしてできた四角形の中に納まったゲームオブジェクトを選択する方法がそれっぽくできたので紹介します。

やりたいことはこういうのですな。

ブログ

ウォークラフトとか、スタークラフトとかエイジオブエンパイアとかにありそうな感じの。
いわゆるRTSの複数ユニット選択のやり方ですね。
マウスクリックとドラッグで良い感じの四角形ができて、こうピーって。
これ作ろうと思ったら、こういう問題があるんですよね。

ブログ1

マウスがクリックしたところはスクリーンの座標として扱われます。
で、ゲームオブジェクトたちの座標というのはワールド座標として扱われます。
まあ当然といえば当然なんですけど、ワールド座標とスクリーン座標は別物なんですよね。
マウスがクリックしたところをワールド座標として扱うには、変換してあげないといけないわけですね。

で、次にマウスがクリックとドラッグで作った四角形の範囲内にいるobjectをどうやって判定するか、という話になるんですよね。
四角形のオブジェクトを作って、大きさを反映させてやって作る、というのも一つの手ですが僕は失敗しました。
要するにこういうことだったんですよね。

ブログ4

オブジェクトの座標がマウスのクリックとドラッグで作った四角形の始点と終点内に収まっているかどうか、で選択されたかを判定します。

つまり、

始点のx座標<=オブジェクトのx座標<=終点のx座標
始点のy座標<=オブジェクトのx座標<=終点のx座標

これで作った四角形内にいる、と判定するわけです。

以下その判定用のデモコードです。
そのままコピペで使えます。
なかなかマッチョでヤングなコードですけど。

/////////////////////////////////////////////////////

using UnityEngine;
using System.Collections;

public class SelectObjectSystem : MonoBehaviour {

public Vector3 startPos;
public Vector3 endPos;
public float s_x;//始点x座標
public float s_y;//始点y座標
public float e_x;//終点x座標
public float e_y;//終点y座標

public GameObject[] gameobject;//デモ用のオブジェクト格納

// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update () {
click ();
}

void click(){

       //左クリックしたときと離した時とで疑似的なドラッグ操作にしてます。
//左クリックしたとき
if (Input.GetMouseButtonDown (0)) {
startPos=Input.mousePosition;//クリックした位置
startPos.z=10f;//マウス座標の奥行の設定
startPos=Camera.main.ScreenToWorldPoint(startPos);//スクリーン座標をワールド座標に
//↓左を離した時
} else if (Input.GetMouseButtonUp (0)) {
endPos=Input.mousePosition;
endPos.z=10f;
endPos=Camera.main.ScreenToWorldPoint(endPos);
unitpositionculiculate();//オブジェクトの位置を判定するメソッド
}

}

void unitpositionculiculate(){

s_x = startPos.x;
s_y = startPos.y;
e_x = endPos.x;
e_y = endPos.y;
//初期の始点と終点のX座標で、始点が終点よりも大きかったら終点の座標と入れ替え
if(startPos.x>endPos.x){
s_x=endPos.x;
e_x=startPos.x;
}
//初期の始点と終点のY座標で、始点が終点よりも大きかったら終点の座標と入れ替え
if(startPos.y>endPos.y){
s_y=endPos.y;
e_y=startPos.y;
}
//for文で判定を行う
for (int i=0; i<3; i++) {
Vector3 posi = gameobject [i].transform.position;
//条件式
if (s_x<=posi.x&&e_x>=posi.x&&s_y<=posi.y&&e_y>=posi.y) {
Debug.Log("範囲内にいます"+gameobject[i].name);
}
}

}
}

/////////////////////////////////////////////////////
補足

~~~~~~わちゃわちゃ色々

startPos.z=10f;//マウス座標の奥行の設定
startPos=Camera.main.ScreenToWorldPoint(startPos);//スクリーン座標をワールド座標に

~~~~~~わちゃわちゃ色々

この部分はマウスのスクリーン座標をワールド座標に変換しています。
このときZの値を設定しているのはスクリーン座標にZ軸は存在しない(つまりVectior2型、XとY座標のみ)ので、Vector3型に収めるとZが0になって、ものすごく小さな範囲でしか値をとれないというか、どこの値なのか与り知らぬ謎の値をとります。認知して、と言われてもUnityは僕の子じゃないので無理です。


・unitpositionculiculate()について
ラクダキャスティングしてないのですごい平坦な名前で見づらいですね。
ここでは始点と終点の座標の判定の祭に、始点のXとY座標が終点よりも大きいかった場合に条件式を実行できないので、値を入れ替える、ということをしています。

インスペクターの設定と、実行時の結果はどうなるかは以下の画像のとおりです。

ブログ2

ブログ3

これでウォークラフトが作れますね!わーい!


結構力押しだったので、まぁ当然ですけど問題点もあります。

◎このやり方の問題点

・カメラに点ぐらいにしか映っていないユニットでも範囲内にいたら選んでしまう。(Z軸は関係ないので)
・範囲外のユニットも判定の対象になっている。
・膨大な数のユニットを用意した場合、毎回その数だけ判定することになる。


こんなところでしょうか。
でも四角形作ってその範囲のユニットを全選択!はやりたかったことなので、超うれしいですね。
あとカッコいいし。
ピーッシュッキャキーンって。


改善点とかあれば、教えてもらえるとうれしいです。

2015/07/19 00:48 はーさん

UnityのVector3.angleの話+継承とusingの違い


またコメントをもらいました。
ありがとうございます。もっと早くにブログで色々書けばよかった・・・

> 継承はびっくりするほど関係ないよ。
> UnityEngine.UI.Imageって書くのが面倒だから省略できるようにするのがusing。
> だからugingの行を消して全部のImageにUnityEngine.UI.って書いても動くよ。

ほえぇ~・・・初耳でした。
つまり両者は

using宣言→「~っていう機能を使うときに、普通に書くと大変だから楽するよ。」宣言
継承→「別に作ってある○○ってヤツの持ってる関数とか変数を使わせてもらうよ。」宣言

こういう感じなのでしょう。
こう考えると、確かに全然関係ありませんね。
継承することは他のところから機能(関数)を引っ張ってくることってイメージだったんで、この宣言を使って初めて色々使えるもんだと思っていました。勘違いはーさんでございました。wrongHasan.

また1つ知恵を得ました。ありがとうございます。






さて、今回はゴリ押しまでの過程の話です。

戦車の砲塔は塔と砲身のセットでございます。
塔を回して、砲身を上下させる。

せっかく有料アセットを買ったのでやりてぇ!リアルな挙動を作りてぇ!そう思った調査団は捜査に乗り出した・・・

えーと、まず僕はこう考えました。

砲塔部分と砲身部分を分けて、砲身を砲塔の子オブジェクトにさせて、砲塔と敵キャラクターの座標を使って二点間の向きとか角度をこう・・・Unityの力で良い感じに取り出して、それを砲身のrotation.xにぶち込んでやる作戦を考えました。

そこで、何か二点間の角度や向きをとれるものはないものか、と探していると出会いました。

Vector3.angle()

Unityってホントなんでもありますね。
二点間の距離から角度、みたいな調べ方したら出てきました。

この関数は引数が二つ存在します。
Vector3.angle(a,b);

aはfrom。どこから、か。
bはto。どこへ、か。

このaからbの距離から、角度を求め・・・るのではなく。
どうも、この関数は、二点のベクトルが作る角度を求めるものらしい。
しかもこの角度、0~180度の間でとる。あとベクトルの大きさは、向きだけを見るので考慮しない・・・っぽい。
角度が0~180度の間、ということもあって座標の値の+とか-は別に考慮しないらしい。

「aとbのベクトル、この二つのベクトルが作る角度はなんぼですか?」コレを求めるもののようである。

この関数はベクトルの向きを考えるので出発点となるaは、出発点のオブジェクトのforwardをとってあげないとちゃんと角度をとってくれませんでした普通のTransform.positionを入れるとあらぬ方向を向いて大変なことに。
コレは恐らくベクトルの向きを見るんで、三次元座標を向きとして扱うのは正面方向とは違うものなんでしょうね。

さらにbに入れるどこまでか、に関してもそのまんま対象のオブジェクトの座標を入れてもダメだった。
対象の座標から出発点の座標を引いた差を使ってあげないと、これもまた座標が一定の値を超えたりするとあらぬ値を取得した。
対象の座標そのまんまじゃなくて、「対象-出発点」としてあげて、二点間の差のベクトルをとってあげてから、正面方向と差のベクトルの成す角度を取得する、って感じじゃないとちゃんと二つのオブジェクトの向きとその角度を求めれないって感じですかね。

数学あんまり分からないので憶測が多いですけど。

使い方的には・・・

//targetPositionには目標の座標を入れます
Vector3 start = transform.TransformDirection(Vector3.forward);
Vector3 target = targetPosition-this.transform.position;
Vector3.angle(start,target);

こんな感じ。
これで二つのオブジェクトの座標から、ベクトルが成す角を求めることができます。

とりあえず、欲しかったものとぜんぜん違いました。



なので、僕は強行手段に出ました。
砲身を敵のいる位置にあわせて上下させる方法を。

そう・・・そいつは・・・

LookAt()

です。

強行手段の手順はこうです。

①LookAt()関数で、砲身を標的ほうへ向かせる。
②しかしLookAt()はyとかzも動くので、砲塔の動きに合わせて、砲身がめっちゃ動いちゃう。
③なので砲身のtransform.rotationをいじって、yとzを常に0にして、x軸だけ動かしちゃおう。


本当なら砲身を敵の位置に合わせて上下なんかはMathfでcosだのなんだの内積がうんぬんかんぬんでやるんでしょうけども。
ですが、ここはUnity。せっかく使えるんですから、使わせてもらいます。
LookAt()実行後のtransform.rotationを求めて、Quartanion型の変数を作って、そこに格納。
そして、変数からyとzをドット技法で個別で変更。
あとは変更したものを砲身のrotationにぶちこんで終わりです。

ずいぶんと回りくどいやり方なんですけど、Transformの、ハマりポインツのせいなんですよね。

たとえば・・・

Transform.position.x=1f;

これはエラーを吐きます。
UnityはTransformの値X,Y,Zを個別で直接変えることを許してくれません。
なので、こんな具合にしてあげます。

Vector3 tpos=transform.position;
tpos.x=1f;
transform.position=tpos;

これで、transformのpositionのxだけを、1に変えることができます。
ほかのtransformの要素も同じように、個別で変えたかったら変数を作って、そこに格納して、ドット技法なりを使って変えてからtransformへ返してあげる、といった感じです。

transformのpositionはVector3型です。
で、rotationはQuaternion型です。
ScaleはVector3型です。
大きさはベクトルの長さで表しているのでしょうか。


最近の収穫はこんな感じです。
また何かあったら書いておきます。
2015/07/15 23:33 はーさん

UnityでHPメーターみたいなものを作った話続編

昨日の記事におコメントがございました。

「Imageのtypeをfilledにすればバーできるよ。」

・・・ほぉ。
そのあと、フォロワーの方からも(画像付きで丁寧に説明してくれました。感謝。)

「filledならこうなりますよ。イメージこんな感じです。(画像」

ワッザ!?

僕はここでそれがどういうことか理解してしまいました。

「わしの天才コードよりも楽な方法があるっちゅーことなんやな・・・!」

ショック1割発見の喜び7割、残り2割はコメントもらえてラッキーな気持ちです。
と、いうわけでfilledを使ったものをひとつ。
どーぞ。




おー・・・ワンダフル。
これを使えばコードもガッツリ短縮できそうな雰囲気。

・作り方

まず、UGUIの使えるUnityであるのが前提条件でございますわよ奥様。
4.6から使えた気がします。

で、UIからImageを選択してオブジェクトを召喚します(4つ星モンスター)

このUGUIのImageとかButtonnとかなんですけどコード上では最初から使えませんので、「using UnityEngine.UI」という具合に上のほうで宣言してあげないとダメなんですよね。
このusingの宣言は、継承とはまた別らしい。
リソースを開放したものを云々かんぬんと言われました。
「あ、わて~っつー機能、このコードで使いますんで!やからよろしく頼むわ!」みたいなもんやと思ってます。

・・・継承と何が違うんだろう?

で、Imageを作ったらその中にImage(Scripts)ってのがあるんです。
そこのImageTypeからfilledを選択します。

で、このfilledタイプなんですけど、fillって英語で埋めるとかいっぱいにする、みたいな意味なんですよね。
たぶん、このタイプのやっていることは設定したSpriteImageをしまう箱があって、その箱をSpriteImageで塗りつぶす、みたいな感じです。
たとえば花の描いてある絵がありますやろ。
その絵をしまう箱がありますねん。
それがImgaeのSpriteですねん。
ほいでその箱にはすだれみたいな、外から見えなくするもんがついてますねん。
で、それに絵をしまいまっしゃろ?
filledタイプなら、すだれを動かして絵をちらちらって見え隠れさせることができるっちゅーわけですねん!

・・・なんか分かりにくい例えだなぁ!

まぁ、やってもらえたらたぶん僕の言いたいことが伝わると思います(投げっぱなしジャーマン)


で、そして次にfill Methodから塗りつぶす動きのパターンを選びます。
動きの軸が縦か横か、あとは90度、180度、360度のどのパターンか。
さっきの埋め込みツイートでは360度パターンにしてます。
fill Originは開始場所です。上下右左から選べます。
で、fillAmountが動きの量です。0~1の間の数値で、float型です。
clockwiseは時計回りか反時計回りか、っぽいですな。onにすると時計回りになります。

あとはコードからこのfillAmountをHPの割合とかを送ってあげて動かしてあげるだけ。
位置の変更とかそういうの一切しなくて良いわけです。
うーん。これは便利。


そういうわけで、昨日ドヤ顔したら見事に粉砕された僕ちゃんなのでした。

どこかの優しいUnity使いの人、ありがとうございました。
おかげで新しいことを知れました。

2015/07/15 02:24 はーさん

ライフバーを作ってみたので、作る過程を載せます。


俺っち頭いい!って思ったことを今から書きます。
ドヤ顔で、中学レベルの数学の話をしちゃいますよ。

さて、Unityでライフバーを作るついでにダメージ計算式を作りました。
その話をしようとおもいます。
コードの話じゃなくて、考え方を載せます。
掲載コードは参考程度に。コピペして使わないほうがいいです。うん!

・ライフバーについて

ゲームでよくあるライフバー。
HPが下がれば黄色くなっていき、赤くなっていって視覚的にプレイヤーにピンチですよ~とかコイツそろそろぶっ倒れるぜ!ざまあないぜ!さあとどめをさすんや!っていうのを伝えてくれる頼もしい味方。
その作り方について、ちょっと書きます。





その1:.HPとバーの1%分の数値を求める。

まずキャラに設定されている最大HPを求めます。
それを適当な変数に代入しておきます。
この変数の1%を求めて、その割合に応じてバーの長さを調節します。
小数点以下なんてプレイヤー側は気にもとめない+作ってる側としてもHPとかダメージを小数点なんて考慮したくないので全部おint型で処理します。

もちろん、ライフバーの長さもHP残量に応じて変化させるので割合を求めます。
と、いうかHPとバーの長さが必ずしも同じ数値になることはないですからね。
ライフバーの長さを100にしてHPを100にし、変化させる処理にしてしまうと、HPがとても多いキャラを作るとそれにあわせてライフバーの長さも変えないといけないので、見た目も悪いうえに作るのが大変です。
割合同士であれば、お互いの数値に依存することなく変化を反映させることができるわけです!
(すごい分かりにくい説明)

こんな感じ)

public int HP;←上のほうで宣言(フィールドって言うんでしたっけアソコ)
変化させるバーの情報//これは、使うものに応じて。UGUI、GUI、NGUIそれぞれの使う値を。
バーのもともとの長さ//もともとの長さから割合を求める。
※バーの例
※RectTransform Lifebar;
※Lifebar=GetComponent();
.....
void lifeBar(){
 double onePar_HP=(double)HP/100;
 バーの情報から変化させる軸の長さ(スケール)を100で割る/X,Y,Z,どれを変えるか
 ※バーの例
 ※doubel bar_x = (double)LifeBar.LocalScale.x/100;
 ※

}

これで1%のHPと長さが分かりました。ぶっちゃけもう勝ちゲーです。

その2:HP残量の割合を求める。

現在HPが最大HPに対してどれくらいの割合なのかを求めます。
先ほど1%分を求めたので、ひたすらfor文かwhile文で足していって、現在HP割合を求める方法と、最大HPから今のHPを引いて、受けた総ダメージを算出し、そこから受けたダメージの割合を求めて100から引いて現在HP割合を求める方法があります。
割合なんで最大値は100%なので100減らすわけです。(ドヤッ

僕は後者でやったんですけど、普通に前者でよくね?

後者のやり方がこうです。
まず、変数で現在HPと最大HPをそれぞれ分けて考えます。
現在HPは適当にnowHPみたいに書いておけばいいんじゃないでしょうか。
最大HP-現在HPで、ダメージを求めます。
そして繰り返し処理でHPの1%分を何回も足していきます。
1%を足しまくるので、何回足したかでダメージの割合を求める作戦です。

※バーの例
※RectTransform Lifebar;
※Lifebar=GetComponent();
.....
void lifeBar(){
 double onePar_HP=(double)HP/100;
 バーの情報から変化させる軸の長さ(スケール)を100で割る/X,Y,Z,どれを変えるか
 ※バーの例
 ※doubel bar_x = (double)LifeBar.LocalScale.x/100;
 ※
int damegeParcent=0//ダメージの割合を格納する変数
double a=0//足しまくるのを格納する変数
while(a  a+=onePar_HP;
damegeParcent++;
}
double hpParcent=100-damageParcent;

}

その3:割合に応じて長さを変えるんよ。

現在HPの割合を求めたので、同じような手段で長さを変えるための割合を求めます。
繰り返し処理で、何回も足す作戦です。
元の長さから、HP1%ごとに元の長さの1%を何回も何回も引いていく作戦です。

※バーの例
※RectTransform Lifebar;
※Lifebar=GetComponent();
.....
void lifeBar(){
 double onePar_HP=(double)HP/100;
 バーの情報から変化させる軸の長さ(スケール)を100で割る/X,Y,Z,どれを変えるか
 ※バーの例
 ※doubel bar_x = (double)LifeBar.LocalScale.x/100;
 ※
int damegeParcent=0//ダメージの割合を格納する変数
double a=0//足しまくるのを格納する変数
while(a  a+=onePar_HP;
damegeParcent++;
}
double hpParcent=100-damageParcent;
float fixScale=0;//バーの長さを調節します。
double x=(double)LifeBar.localScale.x;
for(int i=0;i x-=bar_x;
fixScale+=(float)bar_x;
}

}

その4:スケールを変えて、位置を調整する。

最後に、スケールを変えて位置を調整します。
スケールを変えると、左右で伸び縮みしちゃうので、普通に長さを変えるとそのまんま端っこからズレてしまいます。
なので、ズレた分を直してあげないといけません。
ライフバーのスケールを変更したあとに、変更分に応じて動かしてズレを修正します。

※バーの例
※RectTransform Lifebar;
※Lifebar=GetComponent();
.....
void lifeBar(){
 double onePar_HP=(double)HP/100;
 バーの情報から変化させる軸の長さ(スケール)を100で割る/X,Y,Z,どれを変えるか
 ※バーの例
 ※doubel bar_x = (double)LifeBar.LocalScale.x/100;
 ※
int damegeParcent=0//ダメージの割合を格納する変数
double a=0//足しまくるのを格納する変数
while(a  a+=onePar_HP;
damegeParcent++;
}
double hpParcent=100-damageParcent;
float fixScale=0;//バーの長さを調節します。
double x=(double)LifeBar.localScale.x;
for(int i=0;i x-=bar_x;
fixScale+=(float)bar_x;
}
Vector3 barPos=new Vector3(0,※適当なYの数値,0)
 LifeBar.localScale=new Vector3((float)x1,1);
LifeBar.LocalPosition=new Vector3(barPos.x-fixScale,barPos.y,barPos.z);
}

ノリはこんな感じです。





HPの割合とバーの長さの割合を求める。
割合を基にしてHPに応じたバーの長さを求める。
そしてスケール変化で生じるズレをポジション変更で修正する。

変動するライフバーはこんな具合です。
一連の処理は、UpDateあたりにぶちこんで何回も読み込むよりも変動があった場合のみ読み出してあげるといいかも。

whereのsomeoneのhelpにusefullになれたら嬉しいです。
数値よりもやっぱ視覚的に見えるほうがゲームっぽいし、なによりもぜんぜん微動だにしないライフバーを見て絶望したり、がっつり減るバーを見てキモティってなるんで、視覚的なUIにこだわっていきたいとろろですね。
それにしても、ざるそばの季節ですな。

またなんか作ったら更新します。

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。