読者です 読者をやめる 読者になる 読者になる

夏までにiPhone アプリつくってみっか!

趣味でiPhone/Androidアプリを開発し、日々勉強した事を書いています。オープンワールド系レースゲームをUnityで開発中です。

【Unity5でレースゲーム】AIカーに性格付けをしてライン取りを変化させる

Unity オープンワールドレーシングゲーム

前回の動画で既に実装されているのですが、AIカーに性格付けをして、ライン取りを若干変化させてみました。

途中で事故って2台いなくなってしまいましたが、4台の走るラインが違うので少しレースっぽくなっているのがわかると思います。

前々回の、4台のAIカーが電車のように連なって走る動画と比べるとその違いがわかると思います。

これは、コーナー毎に設置した目標オブジェクトの少しイン側やアウト側を実際の目標としてAIカーを走らせる事で実現しています。inOutLiking変数で設定した位置を中心にmaxDispersionの範囲でランダムな位置が目標地点_shiftedTargetとして設定されます。
AIカーのインスタンス毎にこれらの値を変える事で性格を変える事ができます。

コーナーのイン側、アウト側をプログラムで判断する良い方法が思いつかなかったので、目標オブジェクトをz軸の矢印方向がイン側になるように設置し、それを見るようにしています。

レースっぽくなったのは良いのですが、各々が自分の好きな方へ向かって走るため、2台のラインがクロスすると押し合いになり結果的に2台ともコーナーを曲がりきれずにクラッシュしてしまう事が多くなってしまいました。
次回は、AIカー同士ができるだけ当たらないよう、他車を避ける機能をAIに追加したいと思います。

今回のAIのスクリプトです。

using UnityEngine;
using System.Collections;
using UnityStandardAssets.Vehicles.Car;
using Random = UnityEngine.Random;

public class CarAIDriver : MonoBehaviour
{
    [Range(-1f, 1f)] public float inOutLiking = 0f; // ライン取りの好み。プラスはイン側マイナスはアウト側
    [Range(0f, 1f)] public float maxDispersion = 0f; // ライン取りのばらつきの最大値

    private const float _accelFactor = 0.04f;
    private const float _brakeFactor = 1f;
    private const float _speedFactor = 8f;
    private const float _steerFactor = 0.03f;
    private CarController _carController;
    private Raycaster _raycaster;
    private Rigidbody _rigidbody;
    private Transform[] _raceTrack;
    private Transform _target;
    private SphereCollider _targetCollider;
    private int _currentIndex = 0;
    private int _raceTrackLength;

    private const float _radiusFactor = 0.5f;
    private Vector3 _shiftedTarget;

    void Start()
    {
        _carController = GetComponent<CarController>();
        _raycaster = GetComponent<Raycaster>();
        _rigidbody = GetComponent<Rigidbody>();
        _raceTrack = GameObject.Find("Waypoints").GetComponent<RaceTrack>().raceTrack;
        _raceTrackLength = GameObject.Find("Waypoints").GetComponent<RaceTrack>().raceTrackLength;
        _target = _raceTrack[_currentIndex];
        _targetCollider = _target.GetComponent<SphereCollider>();
        _shiftedTarget = ShiftTarget(_target.position, _target.forward, _targetCollider.radius * _radiusFactor);
    }
	
    void FixedUpdate()
    {
        float currentSpeed = _carController.CurrentSpeed;
        float desiredSpeed = _carController.MaxSpeed;
        // ターゲットにレイがヒット中ならブレーキを掛ける
        if (_raycaster.hittingFront) {
            if (_raycaster.hitF.collider == _targetCollider) {
                desiredSpeed = _targetCollider.radius * _speedFactor;
            }
        }
        float accelBrakeFactor = (desiredSpeed < currentSpeed) ? _brakeFactor : _accelFactor;
        float accel = Mathf.Clamp((desiredSpeed - currentSpeed) * accelBrakeFactor, -1, 1);

        Vector3 localTarget = transform.InverseTransformPoint(_shiftedTarget); //自分からシフト後ターゲットへのベクトル
        float targetAngle = Mathf.Atan2(localTarget.x, localTarget.z) * Mathf.Rad2Deg;
        float steer = Mathf.Clamp(targetAngle * _steerFactor, -1, 1) * Mathf.Sign(currentSpeed);

        _carController.Move(steer, accel, accel, 0f);


        // ターゲットのコライダーの半径の距離まで近づいたら次のターゲットに切り替える
        if (localTarget.magnitude < _targetCollider.radius) {
            _currentIndex = (_currentIndex + 1) % _raceTrackLength;
            _target = _raceTrack[_currentIndex];
            _targetCollider = _target.GetComponent<SphereCollider>();
            _shiftedTarget = ShiftTarget(_target.position, _target.forward, _targetCollider.radius * _radiusFactor);
        }

        Debug.DrawLine(transform.position, _shiftedTarget, Color.blue, 0f, false);
    }

    // originからdirection(ノーマライズされている事)方向に+-rangeの範囲でシフトした位置を返す
    Vector3 ShiftTarget(Vector3 origin, Vector3 direction, float range)
    {
        float offset = Mathf.Clamp(inOutLiking + Random.Range(-maxDispersion / 2f, maxDispersion / 2f), -1f, 1f);
        float magnitude = offset * range;
        return origin + direction * magnitude;
    }
}