Check if two lines intersect - c#

I need to check if two lines intersect. These are currently wrapped in edge colliders.
In my minimal example i using Collider2D.OverlapsCollider
public class EdgeColliderChecker : MonoBehaviour
{
public EdgeCollider2D e1;
public EdgeCollider2D e2;
void Update () {
Collider2D[] results1 = new Collider2D[1];
e1.OverlapCollider(new ContactFilter2D(), results1);
if (results1[0] != null)
{
Debug.Log(results1[0].name);
}
Collider2D[] results2 = new Collider2D[1];
e1.OverlapCollider(new ContactFilter2D(), results2);
if (results2[0] != null) {
Debug.Log(results2[0].name);
}
}
}
This is how i have set up my scene:
As you can see in the picture above the two lines clearly intersect.
The issue is that nothing is outputed to the console.
I am not 100% sure about how ContactFilter should be configured but looking at the documentation it is used for filtering out results. So leaving it blank should include everything.
I really only need to do the check between two lines. So a function that takes them as arguments and returns a bool indicating intersection would be most convenient. Unfortunetaly I could not find such function in Unity.
It should not be overly complicated to construct the function myself but i would prefer to use the functions unity provide as much as possible. So consider this more of a unity related question than a math related one.
EDIT:
Using Collider2D.IsTouching(Collider2D) does not seem to work either. I use the same setup as before with this code instead:
public class EdgeColliderChecker : MonoBehaviour
{
public EdgeCollider2D e1;
public EdgeCollider2D e2;
void Update () {
if (e1.IsTouching(e2)) {
Debug.Log("INTERSECTION");
}
}
}
Edit 2:
I tried creating my own method for this:
public static class EdgeColliderExtentions {
public static List<Collider2D> GetInterSections(this EdgeCollider2D collider)
{
List<Collider2D> intersections = new List<Collider2D>();
Vector2[] points = collider.points;
for (int i = 0; i < points.Length - 1; i++)
{
Vector2 curr = collider.transform.TransformPoint(points[i]);
Vector2 next = collider.transform.TransformPoint(points[i + 1]);
Vector2 diff = next - curr;
Vector2 dir = diff.normalized;
float distance = diff.magnitude;
RaycastHit2D[] results = new RaycastHit2D[30];
ContactFilter2D filter = new ContactFilter2D();
Debug.DrawLine(curr, curr + dir * distance, Color.red, 1 / 60f);
int hits = Physics2D.Raycast(curr, dir, filter, results, distance);
for (int j = 0; i < hits; i++)
{
Collider2D intersection = results[j].collider;
if (intersection != collider)
{
intersections.Add(intersection);
}
}
}
return intersections;
}
}
EdgeColliderChecker:
public class EdgeColliderChecker : MonoBehaviour
{
public EdgeCollider2D e1;
void Update ()
{
List<Collider2D> hits = e1.GetInterSections();
if (hits.Count > 0) {
Debug.Log(hits.Count);
}
}
}
Still nothing. Even though the points i calculate align perfectly with the collider:
I did the math for it and it seems to work ok, not tested very thorougly. The intersection check is a bit choppy if it is run while the colliders are moving around:
public class Line {
private Vector2 start;
private Vector2 end;
public Line(Vector2 start, Vector2 end)
{
this.start = start;
this.end = end;
}
public static Vector2 GetIntersectionPoint(Line a, Line b)
{
//y = kx + m;
//k = (y2 - y1) / (x2 - x1)
float kA = (a.end.y - a.start.y) / (a.end.x - a.start.x);
float kB = (b.end.y - b.start.y) / (b.end.x - b.start.x);
//m = y - k * x
float mA = a.start.y - kA * a.start.x;
float mB = b.start.y - kB * b.start.x;
float x = (mB - mA) / (kA - kB);
float y = kA * x + mA;
return new Vector2(x,y);
}
public static bool Intersects(Line a, Line b)
{
Vector2 intersect = GetIntersectionPoint(a, b);
if (Vector2.Distance(a.start, intersect) < Vector2.Distance(a.start, a.end) &&
Vector2.Distance(a.end, intersect) < Vector2.Distance(a.start, a.end))
{
return true;
}
return false;
}
}
public static class EdgeColliderExtentions
{
public static bool Intersects(this EdgeCollider2D collider, EdgeCollider2D other)
{
Vector2[] points = collider.points;
Vector2[] otherPoints = other.points;
for (int i = 0; i < points.Length - 1; i++)
{
Vector2 start = collider.transform.TransformPoint(points[i]);
Vector2 end = collider.transform.TransformPoint(points[i + 1]);
Line line = new Line(start, end);
for (int j = 0; j < otherPoints.Length - 1; j++)
{
Vector2 otherStart = other.transform.TransformPoint(otherPoints[i]);
Vector2 otherEnd = other.transform.TransformPoint(otherPoints[i + 1]);
Line otherLine = new Line(otherStart, otherEnd);
if (Line.Intersects(line, otherLine))
{
return true;
}
}
}
return false;
}
}
But I'd really like to use something provided by unity instead.

Use Collider.bounds.Intersects(Collider.bounds) to determine if two bounds are intersecting:
void Update () {
if (e1.bounds.Intersects(e2.bounds)) {
Debug.Log("Bounds intersecting");
}
}
This unfortunately won't let you know if the edges are intersecting. However, if this tests false, you can skip testing the edges.

I realized that i could exhange one of the edge colliders for a polygon collider for my use case.
Using a polygon and an edge collider with Collider2D.OverlapsCollider() works as expected.
I don't know if i should accept this as an answer or not because it does not solve the original question about finding a funciton in unity for line intersection.

You could get a relatively close approximation with the following:
You could create a script that adds several small box colliders evenly along the line, the more the better. And then just do normal collision detection. But the more boxes (higher precision), the more costly computation-wise.

Related

Calculate bounce velocity on a line

I'm trying to implement bounce physics on a ball in my game using MonoGame c#. I've googled plenty but I'm unable to understand how to do this.
The circle should be able to hit any of the red lines and bounce realistically (not just invert the velocity).
I'm using this code to detect collision:
public bool IntersectCircle(Vector2 pos, float radius, out Vector2 circleWhenHit)
{
circleWhenHit = default;
// find the closest point on the line segment to the center of the circle
var line = End - Start;
var lineLength = line.Length();
var lineNorm = (1 / lineLength) * line;
var segmentToCircle = pos - Start;
var closestPointOnSegment = Vector2.Dot(segmentToCircle, line) / lineLength;
// Special cases where the closest point happens to be the end points
Vector2 closest;
if (closestPointOnSegment < 0) closest = Start;
else if (closestPointOnSegment > lineLength) closest = End;
else closest = Start + closestPointOnSegment * lineNorm;
// Find that distance. If it is less than the radius, then we
// are within the circle
var distanceFromClosest = pos - closest;
var distanceFromClosestLength = distanceFromClosest.Length();
if (distanceFromClosestLength > radius)
return false;
// So find the distance that places the intersection point right at
// the radius. This is the center of the circle at the time of collision
// and is different than the result from Doswa
var offset = (radius - distanceFromClosestLength) * ((1 / distanceFromClosestLength) * distanceFromClosest);
circleWhenHit = pos - offset;
return true;
}
And this code when the ball wants to change position:
private void GameBall_OnPositionChange(object sender, GameBallPositionChangedEventArgs e)
{
foreach(var boundary in mapBounds)
{
if (boundary.IntersectCircle(e.TargetPosition, gameBall.Radius, out Vector2 colVector))
{
var normalizedVelocity = Vector2.Normalize(e.Velocity);
var velo = e.Velocity.Length();
var surfaceNormal = Vector2.Normalize(colVector - e.CurrentPosition);
e.Velocity = Vector2.Reflect(normalizedVelocity, surfaceNormal) * velo;
e.TargetPosition = e.CurrentPosition;
break;
}
}
}
This code gives a decent result but I'm not using my boundary positions to calculate an angle.
How do I proceed to take those into account?
EDIT:
I've removed the event based update. I've added collision between players and the ball. This is now my map-update method:
foreach (var entity in circleGameEntities)
{
for (int i = 0; i < interpolatePos; i++)
{
entity.UpdatePosition(gameTime, interpolatePos);
var intersectingBoundaries = mapBounds
.Where(b =>
{
var intersects = b.IntersectCircle(entity.Position, entity.Radius, 0f, out _);
if (intersects)
averageNormal += b.Normal;
return intersects;
}).ToList();
if (intersectingBoundaries.Count > 0)
{
averageNormal.Normalize();
var normalizedVelocity = Vector2.Normalize(entity.Velocity); // Normalisera hastigheten
var velo = entity.Velocity.Length();
entity.Velocity = Vector2.Reflect(normalizedVelocity, averageNormal) * velo * entity.Bounciness;
entity.UpdatePosition(gameTime, interpolatePos);
}
foreach (var otherEntity in circleGameEntities.Where(e => e != entity))
{
if (entity.CollidesWithCircle(otherEntity, out Vector2 d))
{
Vector2 CMVelocity = (otherEntity.Mass * otherEntity.Velocity + entity.Mass * entity.Velocity) / (otherEntity.Mass + entity.Mass);
var otherEntityNorm = otherEntity.Position - entity.Position;
otherEntityNorm.Normalize();
var entityNorm = -otherEntityNorm;
var myVelocity = entity.Velocity;
myVelocity -= CMVelocity;
myVelocity = Vector2.Reflect(myVelocity, otherEntityNorm);
myVelocity += CMVelocity;
entity.Velocity = myVelocity;
entity.UpdatePosition(gameTime, interpolatePos);
var otherEntityVelocity = otherEntity.Velocity;
otherEntityVelocity -= CMVelocity;
otherEntityVelocity = Vector2.Reflect(otherEntityVelocity, entityNorm);
otherEntityVelocity += CMVelocity;
otherEntity.Velocity = otherEntityVelocity;
otherEntity.UpdatePosition(gameTime, interpolatePos);
}
}
}
entity.UpdateDrag(gameTime);
entity.Update(gameTime);
}
This code works quite well but sometimes the objects get stuck inside the walls and eachother.
CircleGameEntity class:
class CircleGameEntity : GameEntity
{
internal float Drag { get; set; } = .9999f;
internal float Radius => Scale * (Texture.Width + Texture.Height) / 4;
internal float Bounciness { get; set; } = 1f;
internal float Mass => BaseMass * Scale;
internal float BaseMass { get; set; }
internal Vector2 Velocity { get; set; }
internal float MaxVelocity { get; set; } = 10;
internal void UpdatePosition(GameTime gameTime, int interpolate)
{
var velocity = Velocity;
if (velocity.X < 0 && velocity.X < -MaxVelocity)
velocity.X = -MaxVelocity;
else if (velocity.X > 0 && velocity.X > MaxVelocity)
velocity.X = MaxVelocity;
if (velocity.Y < 0 && velocity.Y < -MaxVelocity)
velocity.Y = -MaxVelocity;
else if (velocity.Y > 0 && velocity.Y > MaxVelocity)
velocity.Y = MaxVelocity;
Velocity = velocity;
Position += Velocity / interpolate;
}
internal void UpdateDrag(GameTime gameTime)
{
Velocity *= Drag;
}
internal bool CollidesWithCircle(CircleGameEntity otherCircle, out Vector2 depth)
{
var a = Position;
var b = otherCircle.Position;
depth = Vector2.Zero;
float distance = Vector2.Distance(a, b);
if (Radius + otherCircle.Radius > distance)
{
float result = (Radius + otherCircle.Radius) - distance;
depth.X = (float)Math.Cos(result);
depth.Y = (float)Math.Sin(result);
}
return depth != Vector2.Zero;
}
}
The surfaceNormal is not the boundary normal, but the angle between the collision point and the center of the circle. This vector takes the roundness of the ball into account and is the negative of the ball's direction(if normalized) as if it hit the surface head-on, which is not needed unless the other surface is curved.
In the Boundary class calculate the angle and one of the normals in the constructor and store them as public readonly:
public readonly Vector2 Angle; // replaces lineNorm for disabiguity
public readonly Vector2 Normal;
public readonly Vector2 Length;
public Boundary(... , bool inside) // inside determines which normal faces the center
{
// ... existing constructor code
var line = End - Start;
Length = line.Length();
Angle = (1 / Length) * line;
Normal = new Vector2(-Angle.Y,Angle.X);
if (inside) Normal *= -1;
}
public bool IntersectCircle(Vector2 pos, float radius)
{
// find the closest point on the line segment to the center of the circle
var segmentToCircle = pos - Start;
var closestPointOnSegment = Vector2.Dot(segmentToCircle, End - Start) / Length;
// Special cases where the closest point happens to be the end points
Vector2 closest;
if (closestPointOnSegment < 0) closest = Start;
else if (closestPointOnSegment > Length) closest = End;
else closest = Start + closestPointOnSegment * Angle;
// Find that distance. If it is less than the radius, then we
// are within the circle
var distanceFromClosest = pos - closest;
return (distanceFromClosest.LengthSquared() > radius * radius); //the multiply is faster than square root
}
The change position code subset:
// ...
var normalizedVelocity = Vector2.Normalize(e.Velocity);
var velo = e.Velocity.Length();
e.Velocity = Vector2.Reflect(normalizedVelocity, boundary.Normal) * velo;
//Depending on timing and movement code, you may need add the next line to resolve the collision during the current step.
e.CurrentPosition += e.Velocity;
//...
This updated code assumes a single-sided non-moving boundary line as prescribed by the inside variable.
I am not a big fan of C# events in games, since it adds layers of delay (both, internally to C# and during proper use the cast of sender.)
I would be remiss not to mention your abuse of the e variable. e should always be treated as a value type: i.e. read-only. The sender variable should be cast(slow) and used for writing purposes.

What's wrong with my SAT collision response system?

I'm creating a MonoGame 2D engine framework for a platforming game, and I'm having trouble creating the collision response system. Although I've gotten SAT detection to work, the response travels across the actual direction of the static body's edge rather than its normal. Reversing the axes of the normal has not worked for me and does nothing, it has only created glitches involving the body going off screen.
Since I'm trying to make a platformer, I only want the normals of the static body to be considered as directions to respond. For example, if the static body is a box, I only want the moving body to travel on 90 degree normals.
Here is a video of the problem in action: https://www.youtube.com/watch?v=-wyXfZkxis0
And the source for the "Collision" module, which has all of the relevant geometric calculations inside (translation vector algorithm at the bottom):
using System;
using Microsoft.Xna.Framework;
namespace Crossfrog.Ferrum.Engine.Modules
{
public static class Collision
{
public static bool RectsCollide(Rectangle rect1, Rectangle rect2)
{
return
rect1.X <= rect2.X + rect2.Width &&
rect1.Y <= rect2.Y + rect2.Height &&
rect1.X + rect1.Width >= rect2.X &&
rect1.Y + rect1.Height >= rect2.Y;
}
private static float DotProduct(Vector2 v1, Vector2 v2)
{
return (v1.X * v2.X) + (v1.Y * v2.Y);
}
private static Vector2 NormalBetween(Vector2 v1, Vector2 v2)
{
return new Vector2(-(v1.Y - v2.Y), v1.X - v2.X);
}
private struct ProjectionLine
{
public float Start;
public float End;
}
private static ProjectionLine ProjectLine(Vector2[] points, Vector2 normal)
{
var projectionLine = new ProjectionLine() { Start = float.MaxValue, End = float.MinValue };
foreach (var p in points)
{
var projectionScale = DotProduct(p, normal);
projectionLine.Start = Math.Min(projectionScale, projectionLine.Start);
projectionLine.End = Math.Max(projectionScale, projectionLine.End);
}
return projectionLine;
}
private static bool CheckOverlapSAT(Vector2[] shape1, Vector2[] shape2)
{
for (int i = 0; i < shape1.Length; i++)
{
var vertex = shape1[i];
var nextVertex = shape1[(i + 1) % shape1.Length];
var edgeNormal = NormalBetween(vertex, nextVertex);
var firstProjection = ProjectLine(shape1, edgeNormal);
var secondProjection = ProjectLine(shape2, edgeNormal);
if (!(firstProjection.Start <= secondProjection.End && firstProjection.End >= secondProjection.Start))
return false;
}
return true;
}
public static bool ConvexPolysCollide(Vector2[] shape1, Vector2[] shape2)
{
return CheckOverlapSAT(shape1, shape2) && CheckOverlapSAT(shape2, shape1);
}
private static float? CollisionResponseAcrossLine(ProjectionLine line1, ProjectionLine line2)
{
if (line1.Start <= line2.Start && line1.End > line2.Start)
return line2.Start - line1.End;
else if (line2.Start <= line1.Start && line2.End > line1.Start)
return line2.End - line1.Start;
return null;
}
public static Vector2 MTVBetween(Vector2[] mover, Vector2[] collider)
{
if (!ConvexPolysCollide(mover, collider))
return Vector2.Zero;
float minResponseMagnitude = float.MaxValue;
var responseNormal = Vector2.Zero;
for (int c = 0; c < collider.Length; c++)
{
var cPoint = collider[c];
var cNextPoint = collider[(c + 1) % collider.Length];
var cEdgeNormal = NormalBetween(cPoint, cNextPoint);
var cProjected = ProjectLine(collider, cEdgeNormal);
var mProjected = ProjectLine(mover, cEdgeNormal);
var responseMagnitude = CollisionResponseAcrossLine(cProjected, mProjected);
if (responseMagnitude != null && responseMagnitude < minResponseMagnitude)
{
minResponseMagnitude = (float)responseMagnitude;
responseNormal = cEdgeNormal;
}
}
var normalLength = responseNormal.Length();
responseNormal /= normalLength;
minResponseMagnitude /= normalLength;
var mtv = responseNormal * minResponseMagnitude;
return mtv;
}
}
}
Your code is nearly right, just follow these steps and it should work.
Normalize your normal in NormalBetween(). Without this, your projected values are distorted and shouldn't be compared to get the right axis.
return Vector2.Normalize(new Vector2(-(v1.Y - v2.Y), v1.X - v2.X));
Use the same collision expression in CollisionResponseAcrossLine() as in CheckOverlapSAT(). Or just use one detection method for both.
if (line1.Start <= line2.Start && line1.End >= line2.Start) // use the >= operator
return line2.Start - line1.End;
else if (line2.Start <= line1.Start && line2.End >= line1.Start) // use the >= operator
return line2.End - line1.Start;
return null;
Compare the absolute magnitudes inside MTVBetween(). The calculated magnitudes can be negative, when they are pointing to the other direction of the normal.
if (responseMagnitude != null && Math.Abs(responseMagnitude.Value) < Math.Abs(minResponseMagnitude))
The following code is no longer needed, because we already normalized the vector in 1.
//var normalLength = responseNormal.Length();
//responseNormal /= normalLength;
//minResponseMagnitude /= normalLength;
This should get your example to work. But when you try it with two polygons, that have different seperation axes, it won't work, because in the collision response code, you only check for the axes of the static collider. The axes from the mover should also be checked, like you did in the collision detection method CheckOverlapSAT().
Calling the CheckOverlapSAT() method inside MTVBetween() seems to be redundant, you could also interrupt the MTVBetween() method, when any responseMagnitude is null.
And last but not least, consider replacing your CollisionResponseAcrossLine() code with the following:
private static float? CollisionResponseAcrossLine(ProjectionLine line1, ProjectionLine line2)
{
float distToStartOfLine2 = line2.Start - line1.End;
if (distToStartOfLine2 > 0)
return null;
float distToEndOfLine2 = line2.End - line1.Start;
if (distToEndOfLine2 < 0)
return null;
if (-distToStartOfLine2 < distToEndOfLine2) // negate distToStartOfLine2, cause it's always negative
return distToStartOfLine2;
else
return distToEndOfLine2;
}
This also accounts for the scenario of the player being inside an obstacle. It compares the distances to both sides and chooses the smaller one. Previously the player would always go to the same edge in this scenario.
If you want code that only supports AABBs, then you could go a simpler route without relying on the SAT. But I guess you want to support polygons too.

transform.position not returning the good position

I made a code that tell me how many positions can the player see on a 2D map.
When I test the code by moving all around the map and testing differents position, it's working. The next step was to create a heatmap of the numbers of position the player can see for every positions. But when I try to run the code, it return false positions.
I've tried different local and global transform methods, and it didn't work
void HtPnt (Vector2 g)
{
int pnt = 0;
foreach (Vector2 f in Q)
{
bool compt = false;
for (int i = 0; i < criticalPointDebug.Length - 1; i++)
{
if (IsInsideTriangle(f, g, criticalPointDebug[i].location, criticalPointDebug[i + 1].location))
compt = true;
}
if (compt == true)
pnt += 1;
}
}
// criticalPointDebug is the list of the visibility polygon, and here's my test in update() :
if (Input.GetKeyDown(KeyCode.A))
{
for (int x=0;x<5;x++)
{
Debug.Log(position);
transform.position = new Vector2(5 + .5f * x, 4);
HtPnt(position);
Debug.Log(position);
}
}
if (Input.GetKeyDown(KeyCode.Z))
{
Debug.Log(position);
transform.position = new Vector2(5,4);
position = new Vector2(5, 4);
HtPnt(position);
Debug.Log(position);
}
first test is returning the correct value of the position on the player (before runing the test) 5 times and the second return nonsense result. Thanks in advance for enlighting me on my problem !

How can I generate stairs?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateStairs : MonoBehaviour
{
public GameObject stairsPrefab;
public int delay = 3;
public int stairsNumber = 5;
public int stairsHeight = 0;
public Vector3 stairsPosition;
public Vector2 stairsSize;
// Use this for initialization
void Start ()
{
StartCoroutine(BuildStairs());
}
// Update is called once per frame
void Update ()
{
}
private IEnumerator BuildStairs()
{
for (float i = 0; i <= stairsSize.x; i++)
{
for (float k = 0; k <= stairsSize.y; k++)
{
stairsPosition = new Vector3(i, stairsHeight, k);
GameObject stairs = Instantiate(stairsPrefab, stairsPosition, Quaternion.identity);
stairs.transform.localScale = new Vector3(stairsSize.x, 1 , stairsSize.y);
stairsHeight += 1;
yield return new WaitForSeconds(delay);
}
}
}
private void CalculateNextStair()
{
}
}
I messed it up. For example I want to build 5 stairs but the loops are over the stairs size and not number of stairs.
Second it's creating 10 sets of stairs not 5 stairs:
Another problem is how can I make that each stair will be build slowly ? Now it's just Instantiate slowly with delay but how can I generate each stair with delay?
Screenshot of the script inspector:
My current code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateStairs : MonoBehaviour
{
public GameObject stairsPrefab;
public float delay = 0.3f;
public int stairsNumber = 5;
public int stairsPositions = 0;
public int stairsStartPositionHeight = 0;
public float stairsScalingHaight = 1;
public Vector2 stairsPosition;
public Vector2 stairsSize;
// Use this for initialization
void Start()
{
StartCoroutine(BuildStairs());
}
// Update is called once per frame
void Update()
{
}
private IEnumerator BuildStairs()
{
for (float i = 0; i <= stairsNumber; i++)
{
// x=0f, y=z=stairsHeight
stairsPosition = new Vector3(0f, stairsPositions, stairsPositions);
GameObject stairs = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stairs.transform.localScale = new Vector3(
stairsSize.x,
stairsScalingHaight,
stairsSize.y);
stairsStartPositionHeight += 1;
yield return new WaitForSeconds(delay);
}
}
private void CalculateNextStair()
{
}
}
There's no reason to loop over the size of the stairs at all; you want to loop over stairsNumber, which is yet unused in your code.
Also, you don't need to change the x component of your stairs' positions. Keep it at 0f (or whatever you need).
The y and z components of your stairs positions (relative to the starting point) should both be factors of stairHeight. In this particular case, you want them to be equal to stairHeight, so that you get "square" step shapes.
private IEnumerator BuildStairs()
{
for (int i = 0; i <= stairsNumber ; i++) {
// x=0f, y=z=stairsHeight
stairsPosition = new Vector3(0f, stairsHeight, stairsHeight);
GameObject stairs = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stairs.transform.localScale = new Vector3(
stairsSize.x,
1f ,
stairsSize.y);
stairsHeight += 1f;
yield return new WaitForSeconds(delay);
}
}
If you change stairSize to be a Vector3, then you can just use stairSize directly as the localScale, and increment stairsHeight by stairsSize.y instead of just 1f.
If you want to offset the starting position of your stairs, you need to include an offset. I recommend keeping it separate from the height counter until you need to add them.
Also, if you want to have rectangular sized steps, keep a widthFactor to multiply by the height to find how far each step moves horizontally.
Combining these changes might look like this:
Vector3 stairSize;
float stepWidthFactor=1f;
Vector3 stairsStartPosition;
private IEnumerator BuildStairs() {
for (int i = 0; i <= stairsNumber ; i++) {
stairsPosition = new Vector3(
stairsStartPosition.x,
stairsStartPosition.y + stairsHeight,
stairsStartPosition.z + stairsHeight*stepWidthFactor);
GameObject stairs = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stairsHeight += stairsSize.y;
stairs.transform.localScale = stairSize;
yield return new WaitForSeconds(delay);
}
}

Error in Unity: Lightning.SetColors(Color, Color)' must have a body because it is not marked abstract, extern, or partial

The errors is exactly:
Error (1)
Assets/_dinostudios123/Match-Tree Engine/Scripts/Effects/Lightning.cs(7,14): error CS0501: `Lightning.SetColors(Color, Color)' must have a body because it is not marked abstract, extern, or partial
This error is caused by changing the [line.SetColors(color, color);] by having to add public void SetColors (Color start, Color end);
Error (2)
Assets/_dinostudios123/Match-Tree Engine/Scripts/Effects/Lightning.cs(30,14): warning CS0618: UnityEngine.LineRenderer.SetVertexCount(int)' is obsolete: Use numPositions instead.'
Could anyone determine why the error codes are coming up when adding in the line of code to correct the color start and color end and to correct int count per line?
using UnityEngine;
using System.Collections;
// Lightning effect
public class Lightning: MonoBehaviour {
public void SetColors ( //The error is applied to this line of code
Color start,
Color end
);
public void SetVertexCount (
int count
);
public Transform start; // start object
public Transform end; //end object
public int bend = 2; // bends count
public Vector3[] bendPoint; // points of bending
public Color color; // start lightning color
public string sortingLayer;
public int sortingOrder;
LineRenderer line; //LineRenderer Line
float distance = 0f;
float lastTime = -100f;
float frequency = 20f;
bool destroing = false;
Vector3 a;
Vector3 b;
void Start (){
line = GetComponent<LineRenderer>();
bendPoint = new Vector3[bend];
line.SetColors(color, color); //color, color
line.SetVertexCount(bend + 2);
line.GetComponent<Renderer>().sortingLayerName = sortingLayer;
line.sortingOrder = sortingOrder;
transform.parent = GameObject.Find("Slots").transform;
}
void Update (){
if (end == null || !end.gameObject.activeSelf || start == null || !start.gameObject.activeSelf) {
Remove();
return;
}
if (!destroing) {
a = start.position;
b = end.position;
}
distance = (a - b).magnitude;
if (lastTime + 1f/frequency < Time.time) {
lastTime = Time.time;
for (int i = 0; i < bendPoint.Length; i++)
bendPoint[i] = new Vector3((2f * Random.value - 1f) * 0.1f * distance, (2f * Random.value - 1f) * 0.1f * distance, 0f);
}
line.SetPosition(0, a);
for (int i= 1; i < bend + 1; i++) {
line.SetPosition(i, Vector3.Lerp(a, b, (1f * i)/(bend+1)) + bendPoint[i-1]);
}
line.SetPosition(bend + 1, b);
}
public void Remove() {
StartCoroutine(FadeOut());
}
IEnumerator FadeOut (){
if (destroing) yield break;
destroing = true;
while (GetComponent<Animation>().isPlaying) yield return 0;
GetComponent<Animation>().Play("LightningFadeOut");
while (GetComponent<Animation>().isPlaying) yield return 0;
Destroy(gameObject);
}
// function of creating new lightning effect
public static Lightning CreateLightning (int bend, Transform start, Transform end, Color color) {
Lightning newLightning = ContentAssistant.main.GetItem<Lightning> ("Lightning");
newLightning.bend = bend;
newLightning.start = start;
newLightning.end = end;
newLightning.color = color;
return newLightning;
}
}
The compiler is warning you that you've defined methods with parameters but no bodies.
Adjusting your formatting makes it easier to see what it's complaining about:
public void SetColors(Color start, Color end);
public void SetVertexCount(int count);
You need to define bodies for those methods.
public void SetColors(Color start, Color end)
{
// set the starting and ending colors
}
public void SetVertexCount(int count)
{
// set the vertex count
}

Categories

Resources