mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-11-12 17:18:22 +00:00
提交
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pathfinding.Examples {
|
||||
/// <summary>Example script used in the example scenes</summary>
|
||||
[HelpURL("https://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_examples_1_1_door_controller.php")]
|
||||
public class DoorController : MonoBehaviour {
|
||||
private bool open = false;
|
||||
|
||||
public int opentag = 1;
|
||||
public int closedtag = 1;
|
||||
public bool updateGraphsWithGUO = true;
|
||||
public float yOffset = 5;
|
||||
|
||||
Bounds bounds;
|
||||
|
||||
public void Start () {
|
||||
// Capture the bounds of the collider while it is closed
|
||||
bounds = GetComponent<Collider>().bounds;
|
||||
|
||||
// Initially open the door
|
||||
SetState(open);
|
||||
}
|
||||
|
||||
void OnGUI () {
|
||||
// Show a UI button for opening and closing the door
|
||||
if (GUI.Button(new Rect(5, yOffset, 100, 22), "Toggle Door")) {
|
||||
SetState(!open);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetState (bool open) {
|
||||
this.open = open;
|
||||
|
||||
if (updateGraphsWithGUO) {
|
||||
// Update the graph below the door
|
||||
// Set the tag of the nodes below the door
|
||||
// To something indicating that the door is open or closed
|
||||
GraphUpdateObject guo = new GraphUpdateObject(bounds);
|
||||
int tag = open ? opentag : closedtag;
|
||||
|
||||
// There are only 32 tags
|
||||
if (tag > 31) { Debug.LogError("tag > 31"); return; }
|
||||
|
||||
guo.modifyTag = true;
|
||||
guo.setTag = tag;
|
||||
guo.updatePhysics = false;
|
||||
|
||||
AstarPath.active.UpdateGraphs(guo);
|
||||
}
|
||||
|
||||
// Play door animations
|
||||
if (open) {
|
||||
GetComponent<Animation>().Play("Open");
|
||||
} else {
|
||||
GetComponent<Animation>().Play("Close");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e96422fbb088f477baaf6f2ada396863
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
@@ -0,0 +1,36 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Pathfinding.Examples {
|
||||
using Pathfinding.RVO;
|
||||
|
||||
/// <summary>
|
||||
/// Player controlled character which RVO agents will avoid.
|
||||
/// This script is intended to show how you can make NPCs avoid
|
||||
/// a player controlled (or otherwise externally controlled) character.
|
||||
///
|
||||
/// See: Pathfinding.RVO.RVOController
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(RVOController))]
|
||||
[HelpURL("https://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_examples_1_1_manual_r_v_o_agent.php")]
|
||||
public class ManualRVOAgent : MonoBehaviour {
|
||||
RVOController rvo;
|
||||
|
||||
public float speed = 1;
|
||||
|
||||
void Awake () {
|
||||
rvo = GetComponent<RVOController>();
|
||||
}
|
||||
|
||||
void Update () {
|
||||
var x = Input.GetAxis("Horizontal");
|
||||
var y = Input.GetAxis("Vertical");
|
||||
|
||||
var v = new Vector3(x, 0, y) * speed;
|
||||
|
||||
// Override the RVOController's velocity. This will disable local avoidance calculations for one simulation step.
|
||||
rvo.velocity = v;
|
||||
transform.position += v * Time.deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 235da29a51432412ca18f3924e2d4c2d
|
||||
timeCreated: 1466892028
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pathfinding.Examples {
|
||||
/// <summary>
|
||||
/// AI controller specifically made for the spider robot.
|
||||
/// Deprecated: This script has been replaced by Pathfinding.Examples.MineBotAnimation. Any uses of this script in the Unity editor will be automatically replaced by one AIPath component and one MineBotAnimation component.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Seeker))]
|
||||
[System.Obsolete("This script has been replaced by Pathfinding.Examples.MineBotAnimation. Any uses of this script in the Unity editor will be automatically replaced by one AIPath component and one MineBotAnimation component.")]
|
||||
[HelpURL("https://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_examples_1_1_mine_bot_a_i.php")]
|
||||
public class MineBotAI : AIPath {
|
||||
/// <summary>
|
||||
/// Animation component.
|
||||
/// Should hold animations "awake" and "forward"
|
||||
/// </summary>
|
||||
public Animation anim;
|
||||
|
||||
/// <summary>Minimum velocity for moving</summary>
|
||||
public float sleepVelocity = 0.4F;
|
||||
|
||||
/// <summary>Speed relative to velocity with which to play animations</summary>
|
||||
public float animationSpeed = 0.2F;
|
||||
|
||||
/// <summary>
|
||||
/// Effect which will be instantiated when end of path is reached.
|
||||
/// See: OnTargetReached
|
||||
/// </summary>
|
||||
public GameObject endOfPathEffect;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override int OnUpgradeSerializedData (int version, bool unityThread) {
|
||||
if (unityThread) {
|
||||
var components = gameObject.GetComponents<Component>();
|
||||
int index = System.Array.IndexOf(components, this);
|
||||
foreach (System.Type newType in new [] { typeof(AIPath), typeof(MineBotAnimation) }) {
|
||||
var newComp = gameObject.AddComponent(newType);
|
||||
foreach (var field in newComp.GetType().GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)) {
|
||||
var oldField = this.GetType().GetField(field.Name);
|
||||
try {
|
||||
if (oldField != null) field.SetValue(newComp, oldField.GetValue(this));
|
||||
} catch (System.Exception e) {
|
||||
Debug.LogError("Failed to upgrade some fields.\n" + e);
|
||||
}
|
||||
}
|
||||
for (int i = components.Length - 1; i > index; i--) UnityEditorInternal.ComponentUtility.MoveComponentUp(newComp);
|
||||
}
|
||||
GameObject.DestroyImmediate(this);
|
||||
return 0;
|
||||
} else {
|
||||
return base.OnUpgradeSerializedData(version, unityThread);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f35b84b127b849e38b74966118e7e0f
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
@@ -0,0 +1,74 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pathfinding.Examples {
|
||||
/// <summary>
|
||||
/// Animation helper specifically made for the spider robot in the example scenes.
|
||||
/// The spider robot (or mine-bot) which has been copied from the Unity Example Project
|
||||
/// can have this script attached to be able to pathfind around with animations working properly.
|
||||
/// This script should be attached to a parent GameObject however since the original bot has Z+ as up.
|
||||
/// This component requires Z+ to be forward and Y+ to be up.
|
||||
///
|
||||
/// A movement script (e.g AIPath) must also be attached to the same GameObject to actually move the unit.
|
||||
///
|
||||
/// Animation is handled by this component. The Animator component refered to in <see cref="anim"/> should have a single parameter called NormalizedSpeed.
|
||||
/// When the end of path is reached, if the <see cref="endOfPathEffect"/> is not null, it will be instantiated at the current position. However a check will be
|
||||
/// done so that it won't spawn effects too close to the previous spawn-point.
|
||||
/// [Open online documentation to see images]
|
||||
/// </summary>
|
||||
[HelpURL("https://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_examples_1_1_mine_bot_animation.php")]
|
||||
public class MineBotAnimation : VersionedMonoBehaviour {
|
||||
/// <summary>
|
||||
/// Animation component.
|
||||
/// Should hold animations "awake" and "forward"
|
||||
/// </summary>
|
||||
public Animator anim;
|
||||
|
||||
/// <summary>
|
||||
/// Effect which will be instantiated when end of path is reached.
|
||||
/// See: <see cref="OnTargetReached"/>
|
||||
/// </summary>
|
||||
public GameObject endOfPathEffect;
|
||||
|
||||
bool isAtDestination;
|
||||
|
||||
IAstarAI ai;
|
||||
Transform tr;
|
||||
|
||||
public override void OnSyncLoad()
|
||||
{
|
||||
base.OnSyncLoad();
|
||||
ai = GetComponent<IAstarAI>();
|
||||
tr = GetComponent<Transform>();
|
||||
}
|
||||
|
||||
/// <summary>Point for the last spawn of <see cref="endOfPathEffect"/></summary>
|
||||
protected Vector3 lastTarget;
|
||||
|
||||
/// <summary>
|
||||
/// Called when the end of path has been reached.
|
||||
/// An effect (<see cref="endOfPathEffect)"/> is spawned when this function is called
|
||||
/// However, since paths are recalculated quite often, we only spawn the effect
|
||||
/// when the current position is some distance away from the previous spawn-point
|
||||
/// </summary>
|
||||
void OnTargetReached () {
|
||||
if (endOfPathEffect != null && Vector3.Distance(tr.position, lastTarget) > 1) {
|
||||
GameObject.Instantiate(endOfPathEffect, tr.position, tr.rotation);
|
||||
lastTarget = tr.position;
|
||||
}
|
||||
}
|
||||
|
||||
protected void Update () {
|
||||
if (ai.reachedEndOfPath) {
|
||||
if (!isAtDestination) OnTargetReached();
|
||||
isAtDestination = true;
|
||||
} else isAtDestination = false;
|
||||
|
||||
// Calculate the velocity relative to this transform's orientation
|
||||
Vector3 relVelocity = tr.InverseTransformDirection(ai.velocity);
|
||||
relVelocity.y = 0;
|
||||
|
||||
// Speed relative to the character size
|
||||
anim.SetFloat("NormalizedSpeed", relVelocity.magnitude / anim.transform.lossyScale.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8503d71a898994d3288ce1708e2707fe
|
||||
timeCreated: 1516539312
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pathfinding {
|
||||
/// <summary>
|
||||
/// Attach to any GameObject and the object will be clamped to the navmesh.
|
||||
/// If a GameObject has this component attached, one or more graph linecasts will be carried out every frame to ensure that the object
|
||||
/// does not leave the navmesh area.
|
||||
/// It can be used with GridGraphs, but Navmesh based ones are prefered.
|
||||
///
|
||||
/// Note: This has partly been replaced by using an RVOController along with RVONavmesh.
|
||||
/// It will not yield exactly the same results though, so this script is still useful in some cases.
|
||||
/// </summary>
|
||||
[HelpURL("https://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_navmesh_clamp.php")]
|
||||
public class NavmeshClamp : MonoBehaviour {
|
||||
GraphNode prevNode;
|
||||
Vector3 prevPos;
|
||||
|
||||
// Update is called once per frame
|
||||
void LateUpdate () {
|
||||
if (prevNode == null) {
|
||||
var nninfo = AstarPath.active.GetNearest(transform.position);
|
||||
prevNode = nninfo.node;
|
||||
prevPos = transform.position;
|
||||
}
|
||||
|
||||
if (prevNode == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prevNode != null) {
|
||||
var graph = AstarData.GetGraph(prevNode) as IRaycastableGraph;
|
||||
if (graph != null) {
|
||||
GraphHitInfo hit;
|
||||
if (graph.Linecast(prevPos, transform.position, out hit)) {
|
||||
hit.point.y = transform.position.y;
|
||||
Vector3 closest = VectorMath.ClosestPointOnLine(hit.tangentOrigin, hit.tangentOrigin+hit.tangent, transform.position);
|
||||
Vector3 ohit = hit.point;
|
||||
ohit = ohit + Vector3.ClampMagnitude((Vector3)hit.node.position-ohit, 0.008f);
|
||||
if (graph.Linecast(ohit, closest, out hit)) {
|
||||
hit.point.y = transform.position.y;
|
||||
transform.position = hit.point;
|
||||
} else {
|
||||
closest.y = transform.position.y;
|
||||
|
||||
transform.position = closest;
|
||||
}
|
||||
}
|
||||
prevNode = hit.node;
|
||||
}
|
||||
}
|
||||
|
||||
prevPos = transform.position;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c46a80ea33e9a403ea2308975022ec42
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
@@ -0,0 +1,83 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Pathfinding.Examples {
|
||||
/// <summary>Small sample script for placing obstacles</summary>
|
||||
[HelpURL("https://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_examples_1_1_object_placer.php")]
|
||||
public class ObjectPlacer : MonoBehaviour {
|
||||
/// <summary>
|
||||
/// GameObject to place.
|
||||
/// When using a Grid Graph you need to make sure the object's layer is included in the collision mask in the GridGraph settings.
|
||||
/// </summary>
|
||||
public GameObject go;
|
||||
|
||||
/// <summary>Flush Graph Updates directly after placing. Slower, but updates are applied immidiately</summary>
|
||||
public bool direct = false;
|
||||
|
||||
/// <summary>Issue a graph update object after placement</summary>
|
||||
public bool issueGUOs = true;
|
||||
|
||||
float lastPlacedTime;
|
||||
|
||||
/// <summary>Update is called once per frame</summary>
|
||||
void Update () {
|
||||
// Check if P is being pressed.
|
||||
// Don't place objects if ctrl is pressed to avoid conflicts with the pause shortcut (ctrl+shift+P)
|
||||
if (!Input.GetKey(KeyCode.LeftControl) && (Input.GetKeyDown("p") || (Input.GetKey("p") && Time.time - lastPlacedTime > 0.3f))) {
|
||||
PlaceObject();
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown("r")) {
|
||||
StartCoroutine(RemoveObject());
|
||||
}
|
||||
}
|
||||
|
||||
public void PlaceObject () {
|
||||
lastPlacedTime = Time.time;
|
||||
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
||||
RaycastHit hit;
|
||||
|
||||
// Figure out where the ground is
|
||||
if (Physics.Raycast(ray, out hit, Mathf.Infinity)) {
|
||||
Vector3 p = hit.point;
|
||||
GameObject obj = GameObject.Instantiate(go, p, Quaternion.identity) as GameObject;
|
||||
|
||||
if (issueGUOs) {
|
||||
Bounds b = obj.GetComponent<Collider>().bounds;
|
||||
GraphUpdateObject guo = new GraphUpdateObject(b);
|
||||
AstarPath.active.UpdateGraphs(guo);
|
||||
if (direct) {
|
||||
AstarPath.active.FlushGraphUpdates();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator RemoveObject () {
|
||||
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
||||
RaycastHit hit;
|
||||
|
||||
// Check what object is under the mouse cursor
|
||||
if (Physics.Raycast(ray, out hit, Mathf.Infinity)) {
|
||||
// Ignore ground and triggers
|
||||
if (hit.collider.isTrigger || hit.transform.gameObject.name == "Ground") yield break;
|
||||
|
||||
Bounds b = hit.collider.bounds;
|
||||
Destroy(hit.collider);
|
||||
Destroy(hit.collider.gameObject);
|
||||
|
||||
if (issueGUOs) {
|
||||
// In Unity, object destruction is actually delayed until the end of the Update loop.
|
||||
// This means that we need to wait until the end of the frame (or until the next frame) before
|
||||
// we update the graph. Otherwise the graph would still think that the objects are there.
|
||||
yield return new WaitForEndOfFrame();
|
||||
GraphUpdateObject guo = new GraphUpdateObject(b);
|
||||
AstarPath.active.UpdateGraphs(guo);
|
||||
if (direct) {
|
||||
AstarPath.active.FlushGraphUpdates();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 687e3bc0934ac46d3957e59872965f3c
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
@@ -0,0 +1,48 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pathfinding {
|
||||
/// <summary>
|
||||
/// Updates the recast tile(s) it is in at start, needs RecastTileUpdateHandler.
|
||||
///
|
||||
/// If there is a collider attached to the same GameObject, the bounds
|
||||
/// of that collider will be used for updating, otherwise
|
||||
/// only the position of the object will be used.
|
||||
///
|
||||
/// Note: This class needs a RecastTileUpdateHandler somewhere in the scene.
|
||||
/// See the documentation for that class, it contains more information.
|
||||
///
|
||||
/// Note: This does not use navmesh cutting. If you only ever add
|
||||
/// obstacles, but never add any new walkable surfaces then you might
|
||||
/// want to use navmesh cutting instead. See navmeshcutting (view in online documentation for working links).
|
||||
///
|
||||
/// See: RecastTileUpdateHandler
|
||||
/// </summary>
|
||||
[AddComponentMenu("Pathfinding/Navmesh/RecastTileUpdate")]
|
||||
[HelpURL("https://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_recast_tile_update.php")]
|
||||
public class RecastTileUpdate : MonoBehaviour {
|
||||
public static event System.Action<Bounds> OnNeedUpdates;
|
||||
|
||||
void Start () {
|
||||
ScheduleUpdate();
|
||||
}
|
||||
|
||||
void OnDestroy () {
|
||||
ScheduleUpdate();
|
||||
}
|
||||
|
||||
/// <summary>Schedule a tile update for all tiles that contain this object</summary>
|
||||
public void ScheduleUpdate () {
|
||||
var collider = GetComponent<Collider>();
|
||||
|
||||
if (collider != null) {
|
||||
if (OnNeedUpdates != null) {
|
||||
OnNeedUpdates(collider.bounds);
|
||||
}
|
||||
} else {
|
||||
if (OnNeedUpdates != null) {
|
||||
OnNeedUpdates(new Bounds(transform.position, Vector3.zero));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c17ca1d2569424ba1be3606fe1bb5c7d
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,159 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pathfinding {
|
||||
/// <summary>
|
||||
/// Helper for easier fast updates to recast graphs.
|
||||
///
|
||||
/// When updating recast graphs, you might just have plonked down a few
|
||||
/// GraphUpdateScene objects or issued a few GraphUpdateObjects to the
|
||||
/// system. This works fine if you are only issuing a few but the problem
|
||||
/// is that they don't have any coordination in between themselves. So if
|
||||
/// you have 10 GraphUpdateScene objects in one tile, they will all update
|
||||
/// that tile (10 times in total) instead of just updating it once which
|
||||
/// is all that is required (meaning it will be 10 times slower than just
|
||||
/// updating one tile). This script exists to help with updating only the
|
||||
/// tiles that need updating and only updating them once instead of
|
||||
/// multiple times.
|
||||
///
|
||||
/// It is coupled with the RecastTileUpdate component, which works a bit
|
||||
/// like the GraphUpdateScene component, just with fewer options. You can
|
||||
/// attach the RecastTileUpdate to any GameObject to have it schedule an
|
||||
/// update for the tile(s) that contain the GameObject. E.g if you are
|
||||
/// creating a new building somewhere, you can attach the RecastTileUpdate
|
||||
/// component to it to make it update the graph when it is instantiated.
|
||||
///
|
||||
/// If a single tile contains multiple RecastTileUpdate components and
|
||||
/// many try to update the graph at the same time, only one tile update
|
||||
/// will be done, which greatly improves performance.
|
||||
///
|
||||
/// If you have objects that are instantiated at roughly the same time
|
||||
/// but not exactly the same frame, you can use the maxThrottlingDelay
|
||||
/// field. It will delay updates up to that number of seconds to allow
|
||||
/// more updates to be batched together.
|
||||
///
|
||||
/// Note: You should only have one instance of this script in the scene
|
||||
/// if you only have a single recast graph. If you have more than one
|
||||
/// graph you can have more than one instance of this script but you need
|
||||
/// to manually call the SetGraph method to configure it with the correct
|
||||
/// graph.
|
||||
///
|
||||
/// Note: This does not use navmesh cutting. If you only ever add
|
||||
/// obstacles, but never add any new walkable surfaces then you might
|
||||
/// want to use navmesh cutting instead. See navmeshcutting (view in online documentation for working links).
|
||||
/// </summary>
|
||||
[AddComponentMenu("Pathfinding/Navmesh/RecastTileUpdateHandler")]
|
||||
[HelpURL("https://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_recast_tile_update_handler.php")]
|
||||
public class RecastTileUpdateHandler : MonoBehaviour {
|
||||
/// <summary>Graph that handles the updates</summary>
|
||||
RecastGraph graph;
|
||||
|
||||
/// <summary>True for a tile if it needs updating</summary>
|
||||
bool[] dirtyTiles;
|
||||
|
||||
/// <summary>True if any elements in dirtyTiles are true</summary>
|
||||
bool anyDirtyTiles = false;
|
||||
|
||||
/// <summary>Earliest update request we are handling right now</summary>
|
||||
float earliestDirty = float.NegativeInfinity;
|
||||
|
||||
/// <summary>All tile updates will be performed within (roughly) this number of seconds</summary>
|
||||
public float maxThrottlingDelay = 0.5f;
|
||||
|
||||
public void SetGraph (RecastGraph graph) {
|
||||
this.graph = graph;
|
||||
|
||||
if (graph == null)
|
||||
return;
|
||||
|
||||
dirtyTiles = new bool[graph.tileXCount*graph.tileZCount];
|
||||
anyDirtyTiles = false;
|
||||
}
|
||||
|
||||
/// <summary>Requests an update to all tiles which touch the specified bounds</summary>
|
||||
public void ScheduleUpdate (Bounds bounds) {
|
||||
if (graph == null) {
|
||||
// If no graph has been set, use the first graph available
|
||||
if (AstarPath.active != null) {
|
||||
SetGraph(AstarPath.active.data.recastGraph);
|
||||
}
|
||||
|
||||
if (graph == null) {
|
||||
Debug.LogError("Received tile update request (from RecastTileUpdate), but no RecastGraph could be found to handle it");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that tiles which do not strictly
|
||||
// contain this bounds object but which still
|
||||
// might need to be updated are actually updated
|
||||
int voxelCharacterRadius = Mathf.CeilToInt(graph.characterRadius/graph.cellSize);
|
||||
int borderSize = voxelCharacterRadius + 3;
|
||||
|
||||
// Expand borderSize voxels on each side
|
||||
bounds.Expand(new Vector3(borderSize, 0, borderSize)*graph.cellSize*2);
|
||||
|
||||
var touching = graph.GetTouchingTiles(bounds);
|
||||
|
||||
if (touching.Width * touching.Height > 0) {
|
||||
if (!anyDirtyTiles) {
|
||||
earliestDirty = Time.time;
|
||||
anyDirtyTiles = true;
|
||||
}
|
||||
|
||||
for (int z = touching.ymin; z <= touching.ymax; z++) {
|
||||
for (int x = touching.xmin; x <= touching.xmax; x++) {
|
||||
dirtyTiles[z*graph.tileXCount + x] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable () {
|
||||
RecastTileUpdate.OnNeedUpdates += ScheduleUpdate;
|
||||
}
|
||||
|
||||
void OnDisable () {
|
||||
RecastTileUpdate.OnNeedUpdates -= ScheduleUpdate;
|
||||
}
|
||||
|
||||
void Update () {
|
||||
if (anyDirtyTiles && Time.time - earliestDirty >= maxThrottlingDelay && graph != null) {
|
||||
UpdateDirtyTiles();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Update all dirty tiles now</summary>
|
||||
public void UpdateDirtyTiles () {
|
||||
if (graph == null) {
|
||||
new System.InvalidOperationException("No graph is set on this object");
|
||||
}
|
||||
|
||||
if (graph.tileXCount * graph.tileZCount != dirtyTiles.Length) {
|
||||
Debug.LogError("Graph has changed dimensions. Clearing queued graph updates and resetting.");
|
||||
SetGraph(graph);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int z = 0; z < graph.tileZCount; z++) {
|
||||
for (int x = 0; x < graph.tileXCount; x++) {
|
||||
if (dirtyTiles[z*graph.tileXCount + x]) {
|
||||
dirtyTiles[z*graph.tileXCount + x] = false;
|
||||
|
||||
var bounds = graph.GetTileBounds(x, z);
|
||||
|
||||
// Shrink it a bit to make sure other tiles
|
||||
// are not included because of rounding errors
|
||||
bounds.extents *= 0.5f;
|
||||
|
||||
var guo = new GraphUpdateObject(bounds);
|
||||
guo.nnConstraint.graphMask = 1 << (int)graph.graphIndex;
|
||||
|
||||
AstarPath.active.UpdateGraphs(guo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anyDirtyTiles = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5e3ec6ef8d2e4827b1645ee843bbe16
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,36 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pathfinding.Examples {
|
||||
/// <summary>
|
||||
/// Smooth Camera Following.
|
||||
/// \author http://wiki.unity3d.com/index.php/SmoothFollow2
|
||||
/// </summary>
|
||||
public class SmoothCameraFollow : VersionedMonoBehaviour {
|
||||
public Transform target;
|
||||
public float distance = 3.0f;
|
||||
public float height = 3.0f;
|
||||
public float damping = 5.0f;
|
||||
public bool enableRotation = true;
|
||||
public bool smoothRotation = true;
|
||||
public float rotationDamping = 10.0f;
|
||||
public bool staticOffset = false;
|
||||
|
||||
void LateUpdate () {
|
||||
Vector3 wantedPosition;
|
||||
|
||||
if (staticOffset) {
|
||||
wantedPosition = target.position + new Vector3(0, height, distance);
|
||||
} else {
|
||||
wantedPosition = target.TransformPoint(0, height, -distance);
|
||||
}
|
||||
transform.position = Vector3.Lerp(transform.position, wantedPosition, Time.deltaTime * damping);
|
||||
|
||||
if (enableRotation) {
|
||||
if (smoothRotation) {
|
||||
Quaternion wantedRotation = Quaternion.LookRotation(target.position - transform.position, target.up);
|
||||
transform.rotation = Quaternion.Slerp(transform.rotation, wantedRotation, Time.deltaTime * rotationDamping);
|
||||
} else transform.LookAt(target, target.up);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46fb00c7e6ad9485282a146ad398a2a5
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,23 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using Pathfinding;
|
||||
|
||||
namespace Pathfinding.Examples {
|
||||
/// <summary>
|
||||
/// Helper editor script to snap an object to the closest node.
|
||||
/// Used in the "Turn Based" example scene for snapping obstacles to the hexagon grid.
|
||||
/// </summary>
|
||||
[ExecuteInEditMode]
|
||||
[HelpURL("https://arongranberg.com/astar/documentation/stable/class_snap_to_node.php")]
|
||||
public class SnapToNode : MonoBehaviour {
|
||||
void Update () {
|
||||
if (transform.hasChanged && AstarPath.active != null) {
|
||||
var node = AstarPath.active.GetNearest(transform.position, NNConstraint.None).node;
|
||||
if (node != null) {
|
||||
transform.position = (Vector3)node.position;
|
||||
transform.hasChanged = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c0acfdba178b145239678a03b9df938a
|
||||
timeCreated: 1453723051
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,77 @@
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
|
||||
namespace Pathfinding {
|
||||
/// <summary>
|
||||
/// Moves the target in example scenes.
|
||||
/// This is a simple script which has the sole purpose
|
||||
/// of moving the target point of agents in the example
|
||||
/// scenes for the A* Pathfinding Project.
|
||||
///
|
||||
/// It is not meant to be pretty, but it does the job.
|
||||
/// </summary>
|
||||
[HelpURL("https://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_target_mover.php")]
|
||||
public class TargetMover : MonoBehaviour {
|
||||
/// <summary>Mask for the raycast placement</summary>
|
||||
public LayerMask mask;
|
||||
|
||||
public Transform target;
|
||||
IAstarAI[] ais;
|
||||
|
||||
/// <summary>Determines if the target position should be updated every frame or only on double-click</summary>
|
||||
public bool onlyOnDoubleClick;
|
||||
public bool use2D;
|
||||
|
||||
Camera cam;
|
||||
|
||||
public void Start () {
|
||||
//Cache the Main Camera
|
||||
cam = Camera.main;
|
||||
// Slightly inefficient way of finding all AIs, but this is just an example script, so it doesn't matter much.
|
||||
// FindObjectsOfType does not support interfaces unfortunately.
|
||||
ais = FindObjectsOfType<MonoBehaviour>().OfType<IAstarAI>().ToArray();
|
||||
useGUILayout = false;
|
||||
}
|
||||
|
||||
public void OnGUI () {
|
||||
if (onlyOnDoubleClick && cam != null && Event.current.type == EventType.MouseDown && Event.current.clickCount == 2) {
|
||||
UpdateTargetPosition();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Update is called once per frame</summary>
|
||||
void Update () {
|
||||
if (!onlyOnDoubleClick && cam != null) {
|
||||
UpdateTargetPosition();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateTargetPosition () {
|
||||
Vector3 newPosition = Vector3.zero;
|
||||
bool positionFound = false;
|
||||
|
||||
if (use2D) {
|
||||
newPosition = cam.ScreenToWorldPoint(Input.mousePosition);
|
||||
newPosition.z = 0;
|
||||
positionFound = true;
|
||||
} else {
|
||||
// Fire a ray through the scene at the mouse position and place the target where it hits
|
||||
RaycastHit hit;
|
||||
if (cam.pixelRect.Contains(Input.mousePosition) && Physics.Raycast(cam.ScreenPointToRay(Input.mousePosition), out hit, Mathf.Infinity, mask)) {
|
||||
newPosition = hit.point;
|
||||
positionFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (positionFound && newPosition != target.position) {
|
||||
target.position = newPosition;
|
||||
|
||||
if (onlyOnDoubleClick) {
|
||||
for (int i = 0; i < ais.Length; i++) {
|
||||
if (ais[i] != null) ais[i].SearchPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f903c2eb621d8418d88f8dad81b13dc6
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
Reference in New Issue
Block a user