你好我使用A *寻路和我使用艾的例子" aipath"但我有山上的旋转剧本,这使得这个成功
但是我将它开始执行此操作的脚本添加到对象中: https://gfycat.com/gifs/detail/ValidSneakyDogwoodtwigborer 它让它出现了很多故障:
并且wen没有on hill rotaion脚本的罚款: https://gfycat.com/upload/results
这是Ai剧本:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Pathfinding;
using Pathfinding.RVO;
using Pathfinding.Util;
/** AI for following paths.
* This AI is the default movement script which comes with the A* Pathfinding Project.
* It is in no way required by the rest of the system, so feel free to write your own. But I hope this script will make it easier
* to set up movement for the characters in your game.
* This script works well for many types of units, but if you need the highest performance (for example if you are moving hundreds of characters) you
* may want to customize this script or write a custom movement script to be able to optimize it specifically for your game.
* \n
* \n
* This script will try to follow a target transform. At regular intervals, the path to that target will be recalculated.
* It will in the #Update method try to move towards the next point in the path.
* However it will only move in roughly forward direction (Z+ axis) of the character, but it will rotate around it's Y-axis
* to make it possible to reach the target.
*
* \section variables Quick overview of the variables
* In the inspector in Unity, you will see a bunch of variables. You can view detailed information further down, but here's a quick overview.\n
* The #repathRate determines how often it will search for new paths, if you have fast moving targets, you might want to set it to a lower value.\n
* The #target variable is where the AI will try to move, it can be a point on the ground where the player has clicked in an RTS for example.
* Or it can be the player object in a zombie game.\n
* The speed is self-explanatory, so is #rotationSpeed, however #slowdownDistance might require some explanation.
* It is the approximate distance from the target where the AI will start to slow down.\n
* #pickNextWaypointDist is simply determines within what range it will switch to target the next waypoint in the path.\n
*
* Below is an image illustrating several variables as well as some internal ones, but which are relevant for understanding how it works.
* \note The image is slightly outdated, replace forwardLook with pickNextWaypointDist in the image and ignore the circle for pickNextWaypointDist.
*
* \shadowimage{aipath_variables.png}
* This script has many movement fallbacks.
* If it finds an RVOController attached to the same GameObject as this component, it will use that. If it fins a character controller it will also use that.
* Lastly if will fall back to simply modifying Transform.position which is guaranteed to always work and is also the most performant option.
*/
[RequireComponent(typeof(Seeker))]
[AddComponentMenu("Pathfinding/AI/AIPath (2D,3D)")]
[HelpURL("http://arongranberg.com/astar/docs/class_a_i_path.php")]
public class AIPath : AIBase {
/** Determines how often it will search for new paths.
* If you have fast moving targets or AIs, you might want to set it to a lower value.
* The value is in seconds between path requests.
*/
public float repathRate = 0.5F;
/** Target to move towards.
* The AI will try to follow/move towards this target.
* It can be a point on the ground where the player has clicked in an RTS for example, or it can be the player object in a zombie game.
*/
Vector3 target;
/** Enables or disables searching for paths.
* Setting this to false does not stop any active path requests from being calculated or stop it from continuing to follow the current path.
* \see #canMove
*/
public bool canSearch = true;
/** Enables or disables movement.
* \see #canSearch
*/
public bool canMove = true;
public bool enableRotation = true; //delete
/** Maximum velocity.
* This is the maximum speed in world units per second.
*/
public float speed = 3;
/** Rotation speed.
* Rotation is calculated using Quaternion.RotateTowards. This variable represents the rotation speed in degrees per second.
* The higher it is, the faster the character will be able to rotate.
*/
[UnityEngine.Serialization.FormerlySerializedAs("turningSpeed")]
public float rotationSpeed = 360;
/** Distance from the target point where the AI will start to slow down.
* Note that this doesn't only affect the end point of the path
* but also any intermediate points, so be sure to set #pickNextWaypointDist to a higher value than this
*/
public float slowdownDistance = 0.6F;
/** Determines within what range it will switch to target the next waypoint in the path */
public float pickNextWaypointDist = 2;
/** Distance to the end point to consider the end of path to be reached.
* When the end is within this distance then #OnTargetReached will be called and #TargetReached will return true.
*/
public float endReachedDistance = 0.2F;
/** Draws detailed gizmos constantly in the scene view instead of only when the agent is selected and settings are being modified */
public bool alwaysDrawGizmos;
/** Time when the last path request was sent */
protected float lastRepath = -9999;
/** Current path which is followed */
protected Path path;
protected PathInterpolator interpolator = new PathInterpolator();
/** Only when the previous path has been returned should be search for a new path */
protected bool canSearchAgain = true;
/** True if the end of the path has been reached */
public bool TargetReached { get; protected set; }
/** True if the Start function has been executed.
* Used to test if coroutines should be started in OnEnable to prevent calculating paths
* in the awake stage (or rather before start on frame 0).
*/
private bool startHasRun = false;
/** Point to where the AI is heading */
protected Vector3 targetPoint;
protected Vector3 velocity;
/** Rotation speed.
* \deprecated This field has been renamed to #rotationSpeed and is now in degrees per second instead of a damping factor.
*/
[System.Obsolete("This field has been renamed to #rotationSpeed and is now in degrees per second instead of a damping factor")]
public float turningSpeed { get { return rotationSpeed/90; } set { rotationSpeed = value*90; } }
/** Starts searching for paths.
* If you override this function you should in most cases call base.Start () at the start of it.
* \see #Init
*/
protected virtual void Start () {
startHasRun = true;
Init();
target = transform.position;
canMove = false;
}
/** Called when the component is enabled */
protected virtual void OnEnable () {
// Make sure we receive callbacks when paths are calculated
seeker.pathCallback += OnPathComplete;
Init();
}
private void MoveSelectedToCursorPosition()
{
RaycastHit raycastHit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out raycastHit, 1000.0f))
{
target = raycastHit.point;
canMove = true;
}
}
void Init () {
if (startHasRun) {
lastRepath = float.NegativeInfinity;
StartCoroutine(RepeatTrySearchPath());
}
}
public void OnDisable () {
seeker.CancelCurrentPathRequest();
// Release current path so that it can be pooled
if (path != null) path.Release(this);
path = null;
// Make sure we no longer receive callbacks when paths complete
seeker.pathCallback -= OnPathComplete;
}
/** Tries to search for a path every #repathRate seconds.
* \see TrySearchPath
*/
protected IEnumerator RepeatTrySearchPath () {
while (true) yield return new WaitForSeconds(TrySearchPath());
}
/** Tries to search for a path.
* Will search for a new path if there was a sufficient time since the last repath and both
* #canSearchAgain and #canSearch are true and there is a target.
*
* \returns The time to wait until calling this function again (based on #repathRate)
*/
public float TrySearchPath () {
if (Time.time - lastRepath >= repathRate && canSearchAgain && canSearch && target != null) {
SearchPath();
return repathRate;
} else {
float v = repathRate - (Time.time-lastRepath);
return v < 0 ? 0 : v;
}
}
/** Requests a path to the target */
public virtual void SearchPath () {
if (target == null) throw new System.InvalidOperationException("Target is null");
lastRepath = Time.time;
// This is where we should search to
Vector3 targetPosition = target;
canSearchAgain = false;
// Alternative way of requesting the path
//ABPath p = ABPath.Construct(GetFeetPosition(), targetPosition, null);
//seeker.StartPath(p);
// We should search from the current position
seeker.StartPath(GetFeetPosition(), targetPosition);
}
public virtual void OnTargetReached () {
// The end of the path has been reached.
// If you want custom logic for when the AI has reached it's destination
// add it here.
// You can also create a new script which inherits from this one
// and override the function in that script
canMove = false;
}
/** Called when a requested path has finished calculation.
* A path is first requested by #SearchPath, it is then calculated, probably in the same or the next frame.
* Finally it is returned to the seeker which forwards it to this function.\n
*/
public virtual void OnPathComplete (Path _p) {
ABPath p = _p as ABPath;
if (p == null) throw new System.Exception("This function only handles ABPaths, do not use special path types");
canSearchAgain = true;
// Claim the new path
p.Claim(this);
// Path couldn't be calculated of some reason.
// More info in p.errorLog (debug string)
if (p.error) {
p.Release(this);
return;
}
// Release the previous path
if (path != null) path.Release(this);
// Replace the old path
path = p;
// Make sure the path contains at least 2 points
if (path.vectorPath.Count == 1) path.vectorPath.Add(path.vectorPath[0]);
interpolator.SetPath(path.vectorPath);
var graph = AstarData.GetGraph(path.path[0]) as ITransformedGraph;
movementPlane = graph != null ? graph.transform : GraphTransform.identityTransform;
// Reset some variables
TargetReached = false;
// Simulate movement from the point where the path was requested
// to where we are right now. This reduces the risk that the agent
// gets confused because the first point in the path is far away
// from the current position (possibly behind it which could cause
// the agent to turn around, and that looks pretty bad).
interpolator.MoveToLocallyClosestPoint((GetFeetPosition() + p.originalStartPoint) * 0.5f);
interpolator.MoveToLocallyClosestPoint(GetFeetPosition());
}
public virtual Vector3 GetFeetPosition () {
if (controller != null && controller.enabled) {
return tr.TransformPoint(controller.center) - Vector3.up*controller.height*0.5F;
}
return tr.position;
}
/** Called during either Update or FixedUpdate depending on if rigidbodies are used for movement or not */
protected override void MovementUpdate (float deltaTime) {
if (Input.GetMouseButtonDown(1))
{
MoveSelectedToCursorPosition();
}
if (!canMove) return;
if (!interpolator.valid) {
velocity2D = Vector3.zero;
} else {
var currentPosition = tr.position;
interpolator.MoveToLocallyClosestPoint(currentPosition, true, false);
interpolator.MoveToCircleIntersection2D(currentPosition, pickNextWaypointDist, movementPlane);
targetPoint = interpolator.position;
var dir = movementPlane.ToPlane(targetPoint-currentPosition);
var distanceToEnd = dir.magnitude + interpolator.remainingDistance;
// How fast to move depending on the distance to the target.
// Move slower as the character gets closer to the target.
float slowdown = slowdownDistance > 0 ? distanceToEnd / slowdownDistance : 1;
// a = v/t, should probably expose as a variable
float acceleration = speed / 0.4f;
velocity2D += MovementUtilities.CalculateAccelerationToReachPoint(dir, dir.normalized*speed, velocity2D, acceleration, speed) * deltaTime;
velocity2D = MovementUtilities.ClampVelocity(velocity2D, speed, slowdown, true, movementPlane.ToPlane(rotationIn2D ? tr.up : tr.forward));
if (distanceToEnd <= endReachedDistance && !TargetReached) {
TargetReached = true;
OnTargetReached();
}
// Rotate towards the direction we are moving in
var currentRotationSpeed = rotationSpeed * Mathf.Clamp01((Mathf.Sqrt(slowdown) - 0.3f) / 0.7f);
RotateTowards(velocity2D, currentRotationSpeed * deltaTime);
var delta2D = CalculateDeltaToMoveThisFrame(movementPlane.ToPlane(currentPosition), distanceToEnd, deltaTime);
Move(currentPosition, movementPlane.ToWorld(delta2D, verticalVelocity * deltaTime));
velocity = movementPlane.ToWorld(velocity2D, verticalVelocity);
}
}
/** Direction that the agent wants to move in (excluding physics and local avoidance).
* \deprecated Only exists for compatibility reasons.
*/
[System.Obsolete("Only exists for compatibility reasons.")]
public Vector3 targetDirection {
get {
return (targetPoint - tr.position).normalized;
}
}
/** Current desired velocity of the agent (excluding physics and local avoidance but it includes gravity).
* \deprecated This method no longer calculates the velocity. Use the #velocity property instead.
*/
[System.Obsolete("This method no longer calculates the velocity. Use the velocity property instead")]
public Vector3 CalculateVelocity (Vector3 position) {
return velocity;
}
#if UNITY_EDITOR
[System.NonSerialized]
int gizmoHash = 0;
[System.NonSerialized]
float lastChangedTime = float.NegativeInfinity;
protected static readonly Color GizmoColor = new Color(46.0f/255, 104.0f/255, 201.0f/255);
protected override void OnDrawGizmos () {
base.OnDrawGizmos();
if (alwaysDrawGizmos) OnDrawGizmosInternal();
}
void OnDrawGizmosSelected () {
if (!alwaysDrawGizmos) OnDrawGizmosInternal();
}
void OnDrawGizmosInternal () {
var newGizmoHash = pickNextWaypointDist.GetHashCode() ^ slowdownDistance.GetHashCode() ^ endReachedDistance.GetHashCode();
if (newGizmoHash != gizmoHash && gizmoHash != 0) lastChangedTime = Time.realtimeSinceStartup;
gizmoHash = newGizmoHash;
float alpha = alwaysDrawGizmos ? 1 : Mathf.SmoothStep(1, 0, (Time.realtimeSinceStartup - lastChangedTime - 5f)/0.5f) * (UnityEditor.Selection.gameObjects.Length == 1 ? 1 : 0);
if (alpha > 0) {
// Make sure the scene view is repainted while the gizmos are visible
if (!alwaysDrawGizmos) UnityEditor.SceneView.RepaintAll();
if (targetPoint != Vector3.zero) Draw.Gizmos.Line(transform.position, targetPoint, GizmoColor * new Color(1, 1, 1, alpha));
Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
Draw.Gizmos.CircleXZ(Vector3.zero, pickNextWaypointDist, GizmoColor * new Color(1, 1, 1, alpha));
Draw.Gizmos.CircleXZ(Vector3.zero, slowdownDistance, Color.Lerp(GizmoColor, Color.red, 0.5f) * new Color(1, 1, 1, alpha));
Draw.Gizmos.CircleXZ(Vector3.zero, endReachedDistance, Color.Lerp(GizmoColor, Color.red, 0.8f) * new Color(1, 1, 1, alpha));
}
}
#endif
protected override int OnUpgradeSerializedData (int version) {
// Approximately convert from a damping value to a degrees per second value.
if (version < 1) rotationSpeed *= 90;
return 1;
}
}
这是on hill旋转脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class rotationOnHills : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
RaycastHit hit;
Ray GroundRay = new Ray(transform.position, -transform.up);
if (Physics.Raycast(GroundRay, out hit))
{
transform.up = hit.normal;
transform.position = hit.point;
}
}
}
我试图在胶囊网格上添加旋转脚本,如果我不添加胶囊对撞机它不起作用但是如果我没有它在Ai对象上它将无缘无故地飞走,如果我将会发生在胶囊网上有装置和对撞机。
答案 0 :(得分:1)
第二个脚本不起作用,因为不幸的是轮换比这复杂得多。
在第一个脚本中,尝试将其添加到MovementUpdate
RaycastHit hit;
Ray GroundRay = new Ray(transform.position, -transform.up);
if (Physics.Raycast(GroundRay, out hit))
{
transform.lookAt(transform.position + velocity.normalized, hit.normal);
}
这表示变换应该看起来在哪里移动,向上的方向等于命中的正常值
编辑:错过了您以固定速度旋转并且在MovementUpdate
中已经有一些旋转代码。你可以改变
// Rotate towards the direction we are moving in
var currentRotationSpeed = rotationSpeed * Mathf.Clamp01((Mathf.Sqrt(slowdown) - 0.3f) / 0.7f);
>>>RotateTowards(>>velocity2D<<, currentRotationSpeed * deltaTime);<<<
var delta2D = CalculateDeltaToMoveThisFrame(movementPlane.ToPlane(currentPosition), distanceToEnd, deltaTime);
Move(currentPosition, movementPlane.ToWorld(delta2D, verticalVelocity * deltaTime));
velocity = movementPlane.ToWorld(velocity2D, verticalVelocity);
到
// Rotate towards the direction we are moving in
var currentRotationSpeed = rotationSpeed * Mathf.Clamp01((Mathf.Sqrt(slowdown) - 0.3f) / 0.7f);
var delta2D = CalculateDeltaToMoveThisFrame(movementPlane.ToPlane(currentPosition), distanceToEnd, deltaTime);
Move(currentPosition, movementPlane.ToWorld(delta2D, verticalVelocity * deltaTime));
velocity = movementPlane.ToWorld(velocity2D, verticalVelocity);
>>>RotateTowards(>>velocity<<, currentRotationSpeed * deltaTime);<<<
基本上,以3D而不是2D
旋转