
1.制作障碍系统 (1.制作几种类型的障碍物的预制体 ((*.编写障碍物基类,以下几种均继承自基类,含碰撞体,RememberY(生成障碍物时该有多大的Y) ((1.矮障碍,仅碰撞体 ((2.高障碍,仅碰撞体 ((3.可冲破障碍 (((1.除基本碰撞体外,额外包含一个触发器,比碰撞体先检测到马,同时获取马的x速度,大于阈值则给障碍做破碎,用马的力度决定破碎力,关闭碎块和马的碰撞 (((*.导入某2D破碎插件 ((4.人马分离障碍 (((WAIT,需要等待人马分离系统先搭建 (2.编写障碍物生成系统 ((1.每若干时间,生成一个随机一种障碍,若干的范围可控 (((1.设计协程,从预制体列表中随机出一种,并在计算好的位置实例化,随后等待范围内的若干时间,然后检查马的存活情况,若马仍存活,重新调用本协程 ((2.生成的位置:x在相机右侧若干不变距离,y根据障碍物的不同而不同,需要计算保存。 (3.编写障碍物消亡系统 ((1.每个障碍物和碎片都会在离开镜头后被删除
422 lines
11 KiB
C#
422 lines
11 KiB
C#
using UnityEngine;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using Delaunay.Geo;
|
|
using Delaunay.LR;
|
|
|
|
namespace Delaunay
|
|
{
|
|
|
|
public sealed class Site: ICoord, IComparable
|
|
{
|
|
private static Stack<Site> _pool = new Stack<Site> ();
|
|
public static Site Create (Vector2 p, uint index, float weight, uint color)
|
|
{
|
|
if (_pool.Count > 0) {
|
|
return _pool.Pop ().Init (p, index, weight, color);
|
|
} else {
|
|
return new Site (p, index, weight, color);
|
|
}
|
|
}
|
|
|
|
internal static void SortSites (List<Site> sites)
|
|
{
|
|
// sites.sort(Site.compare);
|
|
sites.Sort (); // XXX: Check if this works
|
|
}
|
|
|
|
/**
|
|
* sort sites on y, then x, coord
|
|
* also change each site's _siteIndex to match its new position in the list
|
|
* so the _siteIndex can be used to identify the site for nearest-neighbor queries
|
|
*
|
|
* haha "also" - means more than one responsibility...
|
|
*
|
|
*/
|
|
public int CompareTo (System.Object obj) // XXX: Really, really worried about this because it depends on how sorting works in AS3 impl - Julian
|
|
{
|
|
Site s2 = (Site)obj;
|
|
|
|
int returnValue = Voronoi.CompareByYThenX (this, s2);
|
|
|
|
// swap _siteIndex values if necessary to match new ordering:
|
|
uint tempIndex;
|
|
if (returnValue == -1) {
|
|
if (this._siteIndex > s2._siteIndex) {
|
|
tempIndex = this._siteIndex;
|
|
this._siteIndex = s2._siteIndex;
|
|
s2._siteIndex = tempIndex;
|
|
}
|
|
} else if (returnValue == 1) {
|
|
if (s2._siteIndex > this._siteIndex) {
|
|
tempIndex = s2._siteIndex;
|
|
s2._siteIndex = this._siteIndex;
|
|
this._siteIndex = tempIndex;
|
|
}
|
|
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
|
|
private static readonly float EPSILON = .005f;
|
|
/**
|
|
This ABSOLUTELY has to be public! Otherwise you CANNOT workaround
|
|
the major accuracy-bugs in the AS3Delaunay library (because it does NOT
|
|
use stable, consistent data, sadly: you cannot compare two Vector2 objects
|
|
and get a correct answer to "isEqual", it corrupts them at a micro level :( )
|
|
*/
|
|
public static bool CloseEnough (Vector2 p0, Vector2 p1)
|
|
{
|
|
return Vector2.Distance (p0, p1) < EPSILON;
|
|
}
|
|
|
|
private Vector2 _coord;
|
|
public Vector2 Coord {
|
|
get { return _coord;}
|
|
}
|
|
|
|
public uint color;
|
|
public float weight;
|
|
|
|
private uint _siteIndex;
|
|
|
|
// the edges that define this Site's Voronoi region:
|
|
private List<Edge> _edges;
|
|
internal List<Edge> edges {
|
|
get { return _edges;}
|
|
}
|
|
/**
|
|
which end of each edge hooks up with the previous edge in _edges:
|
|
|
|
This MUST BE exposed - it is absurd to hide this, without it the Site
|
|
is generating corrupt data (the .edges property is meaningless without
|
|
access to this list)
|
|
*/
|
|
private List<Side> _edgeOrientations;
|
|
public List<Side> edgeOrientations {
|
|
get { return _edgeOrientations; }
|
|
}
|
|
// ordered list of points that define the region clipped to bounds:
|
|
private List<Vector2> _region;
|
|
|
|
private Site (Vector2 p, uint index, float weight, uint color)
|
|
{
|
|
// if (lock != PrivateConstructorEnforcer)
|
|
// {
|
|
// throw new Error("Site constructor is private");
|
|
// }
|
|
Init (p, index, weight, color);
|
|
}
|
|
|
|
private Site Init (Vector2 p, uint index, float weight, uint color)
|
|
{
|
|
_coord = p;
|
|
_siteIndex = index;
|
|
this.weight = weight;
|
|
this.color = color;
|
|
_edges = new List<Edge> ();
|
|
_region = null;
|
|
return this;
|
|
}
|
|
|
|
public override string ToString ()
|
|
{
|
|
return "Site " + _siteIndex.ToString () + ": " + Coord.ToString ();
|
|
}
|
|
|
|
private void Move (Vector2 p)
|
|
{
|
|
Clear ();
|
|
_coord = p;
|
|
}
|
|
|
|
public void Dispose ()
|
|
{
|
|
// _coord = null;
|
|
Clear ();
|
|
_pool.Push (this);
|
|
}
|
|
|
|
private void Clear ()
|
|
{
|
|
if (_edges != null) {
|
|
_edges.Clear ();
|
|
_edges = null;
|
|
}
|
|
if (_edgeOrientations != null) {
|
|
_edgeOrientations.Clear ();
|
|
_edgeOrientations = null;
|
|
}
|
|
if (_region != null) {
|
|
_region.Clear ();
|
|
_region = null;
|
|
}
|
|
}
|
|
|
|
public void AddEdge (Edge edge)
|
|
{
|
|
_edges.Add (edge);
|
|
}
|
|
|
|
public Edge NearestEdge ()
|
|
{
|
|
_edges.Sort (delegate (Edge a, Edge b) {
|
|
return Edge.CompareSitesDistances (a, b);
|
|
});
|
|
return _edges [0];
|
|
}
|
|
|
|
public List<Site> NeighborSites ()
|
|
{
|
|
if (_edges == null || _edges.Count == 0) {
|
|
return new List<Site> ();
|
|
}
|
|
if (_edgeOrientations == null) {
|
|
ReorderEdges ();
|
|
}
|
|
List<Site> list = new List<Site> ();
|
|
Edge edge;
|
|
for (int i = 0; i < _edges.Count; i++) {
|
|
edge = _edges [i];
|
|
list.Add (NeighborSite (edge));
|
|
}
|
|
return list;
|
|
}
|
|
|
|
private Site NeighborSite (Edge edge)
|
|
{
|
|
if (this == edge.leftSite) {
|
|
return edge.rightSite;
|
|
}
|
|
if (this == edge.rightSite) {
|
|
return edge.leftSite;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
internal List<Vector2> Region (Rect clippingBounds)
|
|
{
|
|
if (_edges == null || _edges.Count == 0) {
|
|
return new List<Vector2> ();
|
|
}
|
|
if (_edgeOrientations == null) {
|
|
ReorderEdges ();
|
|
_region = ClipToBounds (clippingBounds);
|
|
if ((new Polygon (_region)).Winding () == Winding.CLOCKWISE) {
|
|
_region.Reverse ();
|
|
}
|
|
}
|
|
return _region;
|
|
}
|
|
|
|
private void ReorderEdges ()
|
|
{
|
|
//trace("_edges:", _edges);
|
|
EdgeReorderer reorderer = new EdgeReorderer (_edges, VertexOrSite.VERTEX);
|
|
_edges = reorderer.edges;
|
|
//trace("reordered:", _edges);
|
|
_edgeOrientations = reorderer.edgeOrientations;
|
|
reorderer.Dispose ();
|
|
}
|
|
|
|
private List<Vector2> ClipToBounds (Rect bounds)
|
|
{
|
|
List<Vector2> points = new List<Vector2> ();
|
|
int n = _edges.Count;
|
|
int i = 0;
|
|
Edge edge;
|
|
while (i < n && ((_edges[i] as Edge).visible == false)) {
|
|
++i;
|
|
}
|
|
|
|
if (i == n) {
|
|
// no edges visible
|
|
return new List<Vector2> ();
|
|
}
|
|
edge = _edges [i];
|
|
Side orientation = _edgeOrientations [i];
|
|
|
|
if (edge.clippedEnds [orientation] == null) {
|
|
Debug.LogError ("XXX: Null detected when there should be a Vector2!");
|
|
}
|
|
if (edge.clippedEnds [SideHelper.Other (orientation)] == null) {
|
|
Debug.LogError ("XXX: Null detected when there should be a Vector2!");
|
|
}
|
|
points.Add ((Vector2)edge.clippedEnds [orientation]);
|
|
points.Add ((Vector2)edge.clippedEnds [SideHelper.Other (orientation)]);
|
|
|
|
for (int j = i + 1; j < n; ++j) {
|
|
edge = _edges [j];
|
|
if (edge.visible == false) {
|
|
continue;
|
|
}
|
|
Connect (points, j, bounds);
|
|
}
|
|
// close up the polygon by adding another corner point of the bounds if needed:
|
|
Connect (points, i, bounds, true);
|
|
|
|
return points;
|
|
}
|
|
|
|
private void Connect (List<Vector2> points, int j, Rect bounds, bool closingUp = false)
|
|
{
|
|
Vector2 rightPoint = points [points.Count - 1];
|
|
Edge newEdge = _edges [j] as Edge;
|
|
Side newOrientation = _edgeOrientations [j];
|
|
// the point that must be connected to rightPoint:
|
|
if (newEdge.clippedEnds [newOrientation] == null) {
|
|
Debug.LogError ("XXX: Null detected when there should be a Vector2!");
|
|
}
|
|
Vector2 newPoint = (Vector2)newEdge.clippedEnds [newOrientation];
|
|
if (!CloseEnough (rightPoint, newPoint)) {
|
|
// The points do not coincide, so they must have been clipped at the bounds;
|
|
// see if they are on the same border of the bounds:
|
|
if (rightPoint.x != newPoint.x
|
|
&& rightPoint.y != newPoint.y) {
|
|
// They are on different borders of the bounds;
|
|
// insert one or two corners of bounds as needed to hook them up:
|
|
// (NOTE this will not be correct if the region should take up more than
|
|
// half of the bounds rect, for then we will have gone the wrong way
|
|
// around the bounds and included the smaller part rather than the larger)
|
|
int rightCheck = BoundsCheck.Check (rightPoint, bounds);
|
|
int newCheck = BoundsCheck.Check (newPoint, bounds);
|
|
float px, py;
|
|
if ((rightCheck & BoundsCheck.RIGHT) != 0) {
|
|
px = bounds.xMax;
|
|
if ((newCheck & BoundsCheck.BOTTOM) != 0) {
|
|
py = bounds.yMax;
|
|
points.Add (new Vector2 (px, py));
|
|
} else if ((newCheck & BoundsCheck.TOP) != 0) {
|
|
py = bounds.yMin;
|
|
points.Add (new Vector2 (px, py));
|
|
} else if ((newCheck & BoundsCheck.LEFT) != 0) {
|
|
if (rightPoint.y - bounds.y + newPoint.y - bounds.y < bounds.height) {
|
|
py = bounds.yMin;
|
|
} else {
|
|
py = bounds.yMax;
|
|
}
|
|
points.Add (new Vector2 (px, py));
|
|
points.Add (new Vector2 (bounds.xMin, py));
|
|
}
|
|
} else if ((rightCheck & BoundsCheck.LEFT) != 0) {
|
|
px = bounds.xMin;
|
|
if ((newCheck & BoundsCheck.BOTTOM) != 0) {
|
|
py = bounds.yMax;
|
|
points.Add (new Vector2 (px, py));
|
|
} else if ((newCheck & BoundsCheck.TOP) != 0) {
|
|
py = bounds.yMin;
|
|
points.Add (new Vector2 (px, py));
|
|
} else if ((newCheck & BoundsCheck.RIGHT) != 0) {
|
|
if (rightPoint.y - bounds.y + newPoint.y - bounds.y < bounds.height) {
|
|
py = bounds.yMin;
|
|
} else {
|
|
py = bounds.yMax;
|
|
}
|
|
points.Add (new Vector2 (px, py));
|
|
points.Add (new Vector2 (bounds.xMax, py));
|
|
}
|
|
} else if ((rightCheck & BoundsCheck.TOP) != 0) {
|
|
py = bounds.yMin;
|
|
if ((newCheck & BoundsCheck.RIGHT) != 0) {
|
|
px = bounds.xMax;
|
|
points.Add (new Vector2 (px, py));
|
|
} else if ((newCheck & BoundsCheck.LEFT) != 0) {
|
|
px = bounds.xMin;
|
|
points.Add (new Vector2 (px, py));
|
|
} else if ((newCheck & BoundsCheck.BOTTOM) != 0) {
|
|
if (rightPoint.x - bounds.x + newPoint.x - bounds.x < bounds.width) {
|
|
px = bounds.xMin;
|
|
} else {
|
|
px = bounds.xMax;
|
|
}
|
|
points.Add (new Vector2 (px, py));
|
|
points.Add (new Vector2 (px, bounds.yMax));
|
|
}
|
|
} else if ((rightCheck & BoundsCheck.BOTTOM) != 0) {
|
|
py = bounds.yMax;
|
|
if ((newCheck & BoundsCheck.RIGHT) != 0) {
|
|
px = bounds.xMax;
|
|
points.Add (new Vector2 (px, py));
|
|
} else if ((newCheck & BoundsCheck.LEFT) != 0) {
|
|
px = bounds.xMin;
|
|
points.Add (new Vector2 (px, py));
|
|
} else if ((newCheck & BoundsCheck.TOP) != 0) {
|
|
if (rightPoint.x - bounds.x + newPoint.x - bounds.x < bounds.width) {
|
|
px = bounds.xMin;
|
|
} else {
|
|
px = bounds.xMax;
|
|
}
|
|
points.Add (new Vector2 (px, py));
|
|
points.Add (new Vector2 (px, bounds.yMin));
|
|
}
|
|
}
|
|
}
|
|
if (closingUp) {
|
|
// newEdge's ends have already been added
|
|
return;
|
|
}
|
|
points.Add (newPoint);
|
|
}
|
|
if (newEdge.clippedEnds [SideHelper.Other (newOrientation)] == null) {
|
|
Debug.LogError ("XXX: Null detected when there should be a Vector2!");
|
|
}
|
|
Vector2 newRightPoint = (Vector2)newEdge.clippedEnds [SideHelper.Other (newOrientation)];
|
|
if (!CloseEnough (points [0], newRightPoint)) {
|
|
points.Add (newRightPoint);
|
|
}
|
|
}
|
|
|
|
public float x {
|
|
get { return _coord.x;}
|
|
}
|
|
internal float y {
|
|
get { return _coord.y;}
|
|
}
|
|
|
|
public float Dist (ICoord p)
|
|
{
|
|
return Vector2.Distance (p.Coord, this._coord);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// class PrivateConstructorEnforcer {}
|
|
|
|
// import flash.geom.Point;
|
|
// import flash.geom.Rectangle;
|
|
|
|
static class BoundsCheck
|
|
{
|
|
public static readonly int TOP = 1;
|
|
public static readonly int BOTTOM = 2;
|
|
public static readonly int LEFT = 4;
|
|
public static readonly int RIGHT = 8;
|
|
|
|
/**
|
|
*
|
|
* @param point
|
|
* @param bounds
|
|
* @return an int with the appropriate bits set if the Point lies on the corresponding bounds lines
|
|
*
|
|
*/
|
|
public static int Check (Vector2 point, Rect bounds)
|
|
{
|
|
int value = 0;
|
|
if (point.x == bounds.xMin) {
|
|
value |= LEFT;
|
|
}
|
|
if (point.x == bounds.xMax) {
|
|
value |= RIGHT;
|
|
}
|
|
if (point.y == bounds.yMin) {
|
|
value |= TOP;
|
|
}
|
|
if (point.y == bounds.yMax) {
|
|
value |= BOTTOM;
|
|
}
|
|
return value;
|
|
}
|
|
} |