2021年7月29日木曜日

#6 ピンチアウトピンチイン、スクロールとかの実装


画面を広くすると

ドラッグしてがめんずらしたり、

スクロールボタンで画面を拡大縮小したいしたくなります。


Unityでダブルクリックとかドラッグとか

なんか関数があるのかと思ったら特にないみたいなので

自分で作成


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MouseController : MonoBehaviour
{
private float clickTime = 0;
private bool clickFlg = false;
private GameObject mainCamera;
private Vector3 dragStartPosision;
private float mouseDownTime = 0;

void Start()
{
mainCamera = Camera.main.gameObject;
}
void Update()
{
if (Input.GetMouseButtonDown(0) && !clickFlg)//マウスを押した時
{
mouseDownTime=0;//長押しチェック用カウント初期化
dragStartPosision = Input.mousePosition;//ドラッグチェック用スタート地点
}
if(Input.GetMouseButton(0))//マウス押している間
{
mouseDownTime += Time.deltaTime; //長押しチェック用カウントアップ
}
if (Input.GetMouseButtonUp(0))
{
if (clickFlg)//一度クリックし、時間ないにもう一度クリック
{
doubleClickAction();//ダブルクリック
clickFlg=false;//クリックフラグ解除
}
else
{//クリックしていない状態
clickTime=0;//ダブルクリックチェック用カウンタ初期化
clickFlg=true;//クリックチェックフラグ
if(Vector3.Distance(dragStartPosision, Input.mousePosition) > 1)
{//ドラッグチェック、スタート地点から移動しているか?
MovePoisition();//ドラッグ
clickFlg=false;//クリックフラグ解除
}
else if(mouseDownTime>0.4)
{//長押し時間超えているか?
longClickAction();//長押し
clickFlg=false;//クリックフラグ解除
}
}
}
if(clickFlg)//クリックしてチェック
{
clickTime += Time.deltaTime;//ダブルクリックチェック用カウンタUP
if(clickTime > 0.3)//カウンタリミット
{
singleClickAction();//シングルクリック
clickFlg=false;//クリックフラグ解除
}
}
//ホイルスクロールで拡大縮小
float scroll = 0f;
scroll = Input.mouseScrollDelta.y;
mainCamera.transform.Translate(0, 0, scroll);
}

private void MovePoisition()
{
Debug.Log("ドラッグ");
}
void longClickAction()
{
Debug.Log("長押し");
}
void singleClickAction()
{
Debug.Log("シングル");
}
void doubleClickAction()
{
Debug.Log("ダブル");
}

}


マウスボタンの判定は

押した時、押してる時、離した時

これを利用して

ダブルクリックは1回目クリックを離した時からタイマーを動かし、

短い時間で2回目のクリック(ボタン離れた時)が発生したらダブルクリック

クリックフラグをつけておいて、時間がきたら解除、解除される前にクリックが発生するかを確認。

シングルクリックはダブルクリックにならなかったらシングルなので

時間が来たら解除、のところに解除されるってことはシングルクリック!

この時間が来たらの時間を長くするとシングルクリックの反応が遅くて気持ち悪い


ドラッグは

ボタン押した時に、押した場所を記録

離したときに、その場所からずれていたらドラッグとする


長押しは

ボタン押した時にタイマー初期化

ボタン押している間、タイマーを増やす

離した時に、一定以上押していたら長押しとする。

ただ、ドラッグの時も長押し状態なので、ドラッグと異なりマウスが移動していないことをじょうけんに加える


おまけに

スクロールボタンでカメラのズームとかも実装

 

参考

https://qiita.com/Nakatomo/items/7a1491de39a94fa176b0

2021年7月16日金曜日

#4 敵に見つかると追いかけれるようにする

こんにちは!ぴちおです。

前回敵キャラを徘徊させるようにしました。

今回は敵キャラがプレイヤーを見つけると追いかけてくる仕組みを作ります。


仕組みとしては

敵キャラからレイを飛ばして

プレイヤーを見つけたら、プレイヤーの場所にNavMeshで移動

視界から消えた場合、プレイヤーの場所に到着したら、徘徊モードに戻る。


まずは、ますは敵の探索範囲を作ります。

敵オブジェクト[enemy]の下に

空のオブジェクト[CollisionDetector]を作成

スフィアコライダーを追加、スクリプト「searchPlayer」を作成

中身を書いていきます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SearchPlayer : MonoBehaviour
{
EnemyController script; //UnityChanScriptが入る変数
private RaycastHit hit;
// Start is called before the first frame update
void Start()
{
script = transform.parent.gameObject.GetComponent<EnemyController>();
}

private void OnTriggerStay(Collider other)
{
if (other.CompareTag("Player"))
{
GameObject Target = GameObject.Find("player");
var diff = Target.transform.position - transform.position;
var distance = diff.magnitude;
var direction = diff.normalized;
if(Physics.Raycast(transform.position, direction, out hit, distance))
{
if(hit.transform.gameObject == Target)
{
script.targetPlayer = Target;
}
}
}
}

private void OnTriggerExit(Collider other)
{
script.targetPlayer = null;
}
}

コライダーに接触したら、

タグを確認、プレイヤーであれば、

レイ発射!壁とかないのを確認して、

なければenemyのtargetPlayerにヒットしたプレイヤーをセット。

コライダーから外れたら、targetPlayerを空にする。


{
private float i=0;
private NavMeshAgent agent = null;
[SerializeField] private DestinationController destinationController;
private bool arvFlg = false;
private float waitMaxTime = 5f;
private float waitTime;
private float waitCount = 0f;
public GameObject targetPlayer;

GameObjectを宣言

void Update()
{
if(targetPlayer != null){
destinationController.SetDestination(targetPlayer.transform.position);
agent.SetDestination(destinationController.GetDestination());
}
else
{
if(Vector3.Distance(transform.position, destinationController.GetDestination()) < 1.5f)
{
・・・・

}
}
}

Update内にターゲットが決まっている場合そこを目的地に移動するロジックを追加


壁があると、ちゃんと避けたり、視界を遮ったりします。

あ、もちろん、BAKEし直しとか必要です。




参考にさせていただいたサイト:

 

2021年7月15日木曜日

#3 敵キャラを動かしてみる

こんにちは!ぴちおです。

前回プレイヤーを作成したので、

次は敵キャラを表示したいと思います。


まずは、ヒエラルキー→3Dオブジェクト→カプセル

位置をプレイヤーと被らないように

x:10,y:1,z:10に変更。

コンポーネント追加から

ナビメッシュエージェントと

リッジボディと

新しいスクリプト「enemyController」をセット

スクリプトはScriptフォルダに入れておく


あと、プレイヤーと敵がわかるように色をつけます。

プロジェクトの+からフォルダを作成、名前を「Material」とする

プロジェクトの+からマテリアルの作成

できたマテリアルをCtrl+Dで複製

それぞれ名前を「playerMaterial」「enemyMaterial」とする

わかりやすいようにplayerMaterialの色を青、enemyMaterialの色を赤にする



それぞれ、マテリアルをアタッチする(ドラッグ&ドロップ)

続いて、敵の動きをつけます。

とりあえず、初めは徘徊して、視界にプレイヤーが入ったら追っかけてくるようにします。


まずは徘徊の仕組み。

ランダムに移動可能な場所を取得。

そこまでNav Meshで移動。

到着したら決められた時間待機して、再びランダムな場所を取得して移動を繰り返す。

[NavMeshを使った巡回するNPCのつくりかた]を参考に作成。


クラス用に新規C#を作成「DestinationController」という名前にする。

それをダブルクリックしてスクリプトを作成していきます。

using System.Collections;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DestinationController : MonoBehaviour
{
//public float wanderRange;
//目的地
[SerializeField] private Vector3 destination;

public void CreateDestination(Vector3 pos,float wanderRange)
{
//pos:引数現在地
//wanderRange:ランダム範囲
//ランダムな場所を設定
SetDestination(new Vector3(Random.Range( pos.x - wanderRange, pos.x + wanderRange), 0, Random.Range( pos.z - wanderRange, pos.z + wanderRange)));
}

// 目的地の設定
public void SetDestination(Vector3 position)
{
destination = position;
}

// 目的地の取得
public Vector3 GetDestination()
{
return destination;
}
}

CreateDestinationは

posは現在地、要はenemyのtransform.positionです。

wanderRangeはどのくらいの範囲移動するかです。

続いて、enemyController側を作成。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

[RequireComponent(typeof(NavMeshAgent))]
[RequireComponent(typeof(DestinationController))]

public class enemyController : MonoBehaviour
{
private float i=0;
private NavMeshAgent agent = null;
[SerializeField] private DestinationController destinationController;

void Start()
{
agent = GetComponent<NavMeshAgent>();
destinationController = GetComponent<DestinationController>();
destinationController.SetDestination(transform.position);//スタートを待ち状態から始める
}
void Update()
{
if(Vector3.Distance(transform.position, destinationController.GetDestination()) < 1.5f)
{
do
{
destinationController.CreateDestination(transform.position,10f);//ランダムな行き先設定
i++;
if(i>100){
break;
}//予防策
} while (!RandomWander());//ルートがあるかの確認
}
}

private bool RandomWander() {
//指定した目的地に障害物があるかどうか、そもそも到達可能なのかを確認して問題なければセットする。
//pathPending 経路探索の準備できているかどうか
var path = new NavMeshPath();
bool rFlg = false;
if (!agent.pathPending) {
if (agent.remainingDistance <= agent.stoppingDistance) {
//hasPath エージェントが経路を持っているかどうか
//agent.velocity.sqrMagnitudeはスピード
if (NavMesh.CalculatePath(transform.position, destinationController.GetDestination(), NavMesh.AllAreas, path))
{
agent.SetDestination(destinationController.GetDestination());
rFlg = true;
}
}
}
return rFlg;
}

}

using UnityEngine.AIは忘れないように!!

これから到着したら、ちょっと待機する機能を追加します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

[RequireComponent(typeof(NavMeshAgent))]
[RequireComponent(typeof(DestinationController))]

public class enemyController : MonoBehaviour
{
private float i=0;
private NavMeshAgent agent = null;
[SerializeField] private DestinationController destinationController;
private bool arvFlg = false;
private float waitMaxTime = 5f;
private float waitTime;
private float waitCount = 0f;

void Start()
{
agent = GetComponent<NavMeshAgent>();
destinationController = GetComponent<DestinationController>();
destinationController.SetDestination(transform.position);//スタートを待ち状態から始める

waitTime = Random.Range(1,waitMaxTime);//まちじかんをランダム
}
void Update()
{
if(Vector3.Distance(transform.position, destinationController.GetDestination()) < 1.5f)
{
//到着したら
if(arvFlg)
{
//待機時間過ぎたか?
if(waitCount > waitTime)
{
//過ぎた場合行き先セットしてタイマーとフラグクリア
do
{
destinationController.CreateDestination(transform.position,10f);//ランダムな行き先設定
i++;
if(i>100){
break;
}//予防策
} while (!RandomWander());//ルートがあるかの確認
arvFlg=false;
waitTime = Random.Range(1,waitMaxTime);
waitCount = 0;
}
else
{
//過ぎてない場合、カウント
waitCount += Time.deltaTime;
}
}
else
{
arvFlg=true;
}
}
}

private bool RandomWander() {
//指定した目的地に障害物があるかどうか、そもそも到達可能なのかを確認して問題なければセットする。
//pathPending 経路探索の準備できているかどうか
var path = new NavMeshPath();
bool rFlg = false;
if (!agent.pathPending) {
if (agent.remainingDistance <= agent.stoppingDistance) {
//hasPath エージェントが経路を持っているかどうか
//agent.velocity.sqrMagnitudeはスピード
if (NavMesh.CalculatePath(transform.position, destinationController.GetDestination(), NavMesh.AllAreas, path))
{
agent.SetDestination(destinationController.GetDestination());
rFlg = true;
}
}
}
return rFlg;
}

}

到着したら、到着フラグ立てる、到着フラグ立ってれば待ちカウントアップして、規定値になったら処理を行う。

destinationController.CreateDestination(transform.position,10f); 
ここの10fを長くすると長距離、短くすると短距離になります。
private float waitMaxTime = 5f;
ここの数値を長くすると待ち時間の最長時間が長くなります。
ソースないでランダムで書いてるところをそのまま数値にすると、固定の待ち時間になります。
 

これで、敵キャラがうろちょろします。




参考にさせていただいたサイト:


NavMeshを使った巡回するNPCのつくりかた


【Unity】徘徊する敵をNavigationで作る方法

2021年7月14日水曜日

#2 オブジェクトを動かしてみる

こんにちは!ぴちおです。! 

 キャラクターがいないと始まらないので
 まずは床とキャラクターをセット 
 ヒエラルキー→3Dオブジェクト→平面をクリック 
 そのままだと動かすのにも狭いので 少し大きくします。 
Transformm→スケールでx 3, y 0, z 1にセット 
あと、名前を「ground」に変更しておきます。 

 続いて、キャラクターをセット 
とりあえずはオブジェクトそのまま 
ヒエラルキー→3Dオブジェクト→カプセル 

名前を「player」 タグを「player」 
Transformm→位置をx 0, y 1, z 0にセット 



 









今回はクリックした場所に移動させたいので NavMeshを使っていきます。 
 ウィンドウ→AI→ナビゲーション 
groundを選択して 
ナビゲーションタブのNavigation staticにチェックを入れる 
ベイクタブのベイクをクリック 
 今回床しかないので真っ青になるだけ、 
障害物とかを設定しておくとそこを避けるような形になります。 

 続いてクリックした場所に移動するように設定してきます。 
まずは、「player」を選択して、 
NavMeshエージェントを追加 
インスペクター→コンポーネントの追加、
navと入れると出てくる「ナビメッシュエージェント」を追加 
 次、スクリプト作成 
インスペクター→コンポーネントの追加 新しいスクリプトをクリック、
名前を「playerConntroller」とします。 

 プロジェクトに作成したスクリプトが表示されてます。 
後でわからなくならないようにフォルダ分します。 
 プロジェクトタブの下の➕をクリック、
フォルダをクリック 名称を「script」をします。 
先ほど作成した「playerConntroller」をこのフォルダにドラッグ&ドロップ 

 続いて、「playerConntroller」をダブルクリックしてエディタで開きます。 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class playerController : MonoBehaviour
{
// ナビゲーションエージェント
private NavMeshAgent agent;

// Start is called before the first frame update
void Start()
{
agent = GetComponent<NavMeshAgent>();
agent.SetDestination(transform.position);
}

// Update is called once per frame
void Update()
{
// マウスクリックまたはmouseDownModeがOffの時マウスの位置を移動する位置にする
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 100))
{
agent.destination = hit.point;
}
}
}
}


これで動くのですが、
このままだとわかりづらいので
カメラを修正
 ヒエラルキー→Main Cameraをクリック
インスペクターの値を変更。
位置:x0,y40,z0
回転:x90,y0,z0

上空から見下ろす感じになります。
 

[参考にさせていただいたサイト]

#1 プロジェクト作成

こんにちは!ぴちおです! 

 まずは、Unityでゲームを・・ 
とりあえずはUnityを動かしていきます。 

 インストールについては UnityのページからHubをダウンロードしてください。 
 VSCもvisual studio codeで検索してダウンロードします。 
色々拡張機能あるのでC#のとか色々調べてインストールします。(いつか記事にするかも、とりあえずググって) 

 unity hubから新規作成
プロジェクトは 3Dにして名前を「rts game」で作成をクリック
あ、日本語・・・私は英語苦手なので、日本語にしてます。ただ、サイトの情報見ると英語が多いので英語の方が良いかもしれない 設定方法は Unity→Preferences→Language ここで設定変更
と思ったのですが、サイトに載っている情報のほとんどが英語
また、パラメータとか変更するとき、英語じゃないとわかりづらいのでやっぱり英語環境の方が良さそう。
 
私は1画面で操作するので、 レイアウトはこんな感じ

初心者がUnityでRTS風ゲームを作る

こんにちは!ぴちおです。 

 最終目標UnityでRTSゲームを作る! 

目操作は AOS的なRTS 
 ただ、unity初心者なので、調べながら、苦戦しながらコツコツレベルアップしていく予定。 
なので、記事には間違いや、非効率的なものが上がりますが
 あくまで個人的な備忘録です。 

 なお、過去にPCスペックが低すぎてUnityを断念しました。 
もし、開発するならある程度のスペックがないと辛いです。 

 以下私の開発環境 
 mac book air M1 メモリ16gb 
unity 2020.3.12f1 
C#エディタ:vsc 1.57.1