到着してから「棒立ち」ではなく、ちゃんとベッドに横になったり、椅子に座ったりすると、一気にシミュレーションのリアリティが増します。
これを実現するには、**「家具側にアニメーションの種類と『位置合わせ』の情報を待たせること」**が重要です。
以下に、アニメーションと位置合わせ(Snapping)を組み込んだ実装手順を解説します。
1. 準備:アニメーションの状態管理 (Animator)
まず、UnityのAnimator Controllerで、以下のようなステート(状態)とパラメータを作成する必要があります。
必要なパラメータ:
Speed(Float): 移動用(0なら止まる、0.1以上なら歩く)IsSleeping(Bool): 寝ているかどうかIsSitting(Bool): 座っているかどうかDoEat(Trigger): 食べる動作(1回再生)
ステートマシンの構造イメージ:
Plaintext
[Entry] -> [Locomotion (Idle/Walk)]
| ^
v |
[Sleep State] (IsSleeping = true で遷移)
| ^
v |
[Sit State] (IsSitting = true で遷移)
| ^
v |
[Eat State] (DoEat Trigger で遷移)
※ アニメーションデータ(.animファイル)がない場合は、Adobeの無料サービス「Mixamo」から「Idle」「Walking」「Sleeping」「Sitting」「Eating」などをダウンロードして使うのが一般的です。
2. コードの更新
前回のスクリプトをアップグレードします。
A. 家具用スクリプト (SmartObject.cs)
家具に「ここでどんなアニメをするか」と「キャラクターがどこに位置合わせすべきか」という情報を追加します。
C#
using UnityEngine;
public enum NeedType { None, Hunger, Energy, Fun }
// 追加: アニメーションの種類
public enum ActionAnimation { None, Eat, Sleep, Sit }
public class SmartObject : MonoBehaviour
{
[Header("基本設定")]
public string objectName = "Furniture";
public NeedType needType;
public float recoveryAmount = 50f;
public float interactionTime = 3f;
[Header("アニメーション設定")]
public ActionAnimation actionType; // この家具で何のアニメをするか
public Transform interactionPoint; // 追加: 座る/寝る時の正確な位置と向き
// 呼び出し用の位置取得
public Vector3 GetInteractionPosition()
{
// interactionPointが設定されていればその位置、なければ家具の位置
return interactionPoint != null ? interactionPoint.position : transform.position;
}
}
B. AI用スクリプト (HumanLifeAI.cs)
NavMeshAgentを一時停止させ、位置を家具にスナップ(吸着)させてからアニメーションを再生します。
C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using System.Linq;
public class HumanLifeAI : MonoBehaviour
{
// ... (欲求パラメータ変数は前回と同じなので省略) ...
[Header("欲求パラメータ (0-100)")]
public float hunger = 100f;
public float energy = 100f;
public float fun = 100f;
// 省略... (Decay変数など)
private NavMeshAgent agent;
private Animator animator; // 追加: アニメーター
private bool isBusy = false;
void Start()
{
agent = GetComponent<NavMeshAgent>();
animator = GetComponent<Animator>(); // コンポーネント取得
}
void Update()
{
// 移動アニメーションの制御
float speed = agent.velocity.magnitude;
animator.SetFloat("Speed", speed); // Speedパラメータに速度を渡す
// ... (欲求減少とThinkAndActロジックは前回と同じ) ...
DecreaseNeeds();
if (!isBusy) ThinkAndAct();
}
// ... (ThinkAndAct, FindAndInteract, Wander などは前回と同じ) ...
// ... 下記の ExecuteAction だけ書き換えてください ...
// ★ 更新箇所: アニメーション対応版のアクション実行
IEnumerator ExecuteAction(SmartObject target)
{
isBusy = true;
agent.SetDestination(target.GetInteractionPosition());
// 移動中待機
while (agent.pathPending || agent.remainingDistance > agent.stoppingDistance)
{
yield return null;
}
// 1. 到着したらNavMeshAgentの制御を一時的に切る(位置合わせのため)
agent.isStopped = true;
// 2. 位置と回転の補正 (Snapping)
// ベッドや椅子の場合、正確な位置と向きにキャラを移動させる
if (target.interactionPoint != null)
{
// 位置を合わせる(少し時間をかけて補間するとより綺麗ですが、今回は瞬時に)
transform.position = target.interactionPoint.position;
transform.rotation = target.interactionPoint.rotation;
}
// 3. アニメーション再生
PlayActionAnimation(target.actionType, true);
// 4. 行動時間待機
yield return new WaitForSeconds(target.interactionTime);
// 5. アニメーション終了
PlayActionAnimation(target.actionType, false);
// 6. 回復処理とAgent再開
RestoreNeed(target.needType, target.recoveryAmount);
agent.isStopped = false; // 移動再開許可
isBusy = false;
}
// アニメーターへの命令をまとめた関数
void PlayActionAnimation(ActionAnimation type, bool isActive)
{
switch (type)
{
case ActionAnimation.Sit:
animator.SetBool("IsSitting", isActive);
break;
case ActionAnimation.Sleep:
animator.SetBool("IsSleeping", isActive);
break;
case ActionAnimation.Eat:
if (isActive) animator.SetTrigger("DoEat");
// Triggerは一度引けばいいのでfalse時は何もしない
break;
}
}
// ... (RestoreNeed, Wander, DecreaseNeeds は前回と同じ) ...
void RestoreNeed(NeedType type, float amount)
{
switch (type)
{
case NeedType.Hunger: hunger = Mathf.Clamp(hunger + amount, 0, 100); break;
case NeedType.Energy: energy = Mathf.Clamp(energy + amount, 0, 100); break;
case NeedType.Fun: fun = Mathf.Clamp(fun + amount, 0, 100); break;
}
}
void ThinkAndAct()
{
if (hunger < 30) FindAndInteract(NeedType.Hunger);
else if (energy < 20) FindAndInteract(NeedType.Energy);
else if (fun < 40) FindAndInteract(NeedType.Fun);
else Wander();
}
void FindAndInteract(NeedType type)
{
SmartObject[] allObjects = FindObjectsOfType<SmartObject>();
var targetObjects = allObjects.Where(obj => obj.needType == type).ToList();
if (targetObjects.Count > 0)
{
SmartObject nearest = targetObjects.OrderBy(o => Vector3.Distance(transform.position, o.transform.position)).First();
StartCoroutine(ExecuteAction(nearest));
}
else
{
Wander();
}
}
void Wander()
{
if (agent.remainingDistance > agent.stoppingDistance) return;
Vector3 randomDirection = Random.insideUnitSphere * 5f;
randomDirection += transform.position;
NavMeshHit hit;
if (NavMesh.SamplePosition(randomDirection, out hit, 5f, 1)) agent.SetDestination(hit.position);
}
void DecreaseNeeds()
{
hunger -= 5f * Time.deltaTime;
energy -= 2f * Time.deltaTime;
fun -= 3f * Time.deltaTime;
}
}
3. Unityエディタでの設定ポイント(ここが重要)
コードを書いただけでは綺麗に動きません。シーン上の設定が必要です。
Step 1: Interaction Point (位置合わせポイント) を作る
ベッドや椅子の子オブジェクトとして、空のGameObjectを作成し、名前を「Point」などにします。
- ベッドの場合:
- その「Point」を、枕の上に配置します。
- 重要: PointのZ軸(青い矢印)が、キャラの足の向く方向(または頭の方向、アニメーションによる)になるように回転させます。寝るアニメーションが再生されたとき、このPointと同じ向きになります。
SmartObjectコンポーネントのInteraction Point欄に、この「Point」オブジェクトをドラッグ&ドロップします。
Step 2: Smart Object の設定
- ベッド: Action Type を
Sleepに設定。 - 椅子: Action Type を
Sitに設定。 - 冷蔵庫: Action Type を
Eatに設定(Interaction Pointは冷蔵庫の前の床に配置し、冷蔵庫の方を向くように回転させる)。
Step 3: Animator の遷移設定
Animatorウィンドウで、矢印(Transition)の設定を確認します。
- Any State -> Sleep: Condition に
IsSleeping = trueを追加。Has Exit Timeのチェックを外す(即座に寝るため)。 - Sleep -> Locomotion: Condition に
IsSleeping = falseを追加。 - Locomotion -> Sit: Condition に
IsSitting = true。 - Sit -> Locomotion: Condition に
IsSitting = false。
これでどうなる?
実行すると、キャラクターは以下のように動きます。
- 体力が減る。
- ベッドへ歩いていく。
- ベッドの横に着くと、スッと枕の上にワープし、体の向きを整える。
- 「寝るアニメーション」が再生され、動かなくなる。
- 体力が回復した後、むっくり起き上がり(Idleに戻り)、次の行動(冷蔵庫など)へ向かう。
これで一気に「シム」っぽい挙動になります。

