How to select and drag an ellipse in old version of Processing? - drag-and-drop

//The following game has been designed as an educational resource
//for Key Stage 1 and 2 children. Children are the future of
//civil engineering, and to inspire them to get involved in the
//industry is important for innovation. However, today the
//national curriculum is very structured, and many children
//can find themselves falling behind even at the age of 7 or 8.
//It is essential that children can be supported with material
//they find difficult, and given the resources to learn in a
//fun and engaging manner.
//One of the topics that many children struggle to grasp is
//fractions. It is necessary to prevent young children feeling
//like STEM subjects are too difficult for them, so that they
//have the opportunity and confidence to explore science and
//engineering subjects as they move into secondary education and
//careers.
//This game intends to set a precedent for teaching complex
//subjects to children in a simple, but fun and interactive
//manner. It will show them that fractions can be fun, and that
//they are capable, building confidence once they return to
//the classroom.
//The game will work by challenging the user to split a group
//of balls into three buckets depending on the fraction
//displayed on the bucket.
int number_of_balls;
float bucket_1, bucket_2, bucket_3;
int bucket_1_correct, bucket_2_correct, bucket_3_correct;
PVector basket_position, basket_dimensions;
Ball[] array_of_balls;
int linethickness;
//Random generator to give number of balls, ensuring that
//they can be divided into the number of buckets available.
void setup()
{
size(500,500);
linethickness = 4;
number_of_balls = int(random(1,11))*6;
println(number_of_balls);
bucket_1 = 1/6;
bucket_2 = 1/2;
bucket_3 = 1/3;
//Working out the correct answers
bucket_1_correct = number_of_balls*bucket_1;
bucket_2_correct = number_of_balls*bucket_2;
bucket_3_correct = number_of_balls*bucket_3;
println (bucket_1, bucket_2, bucket_3);
println (bucket_1_correct, bucket_2_correct, bucket_3_correct);
//Creating the basket
basket_position = new PVector(width/4, height/8);
basket_dimensions = new PVector(width/2, height/4);
//Creating the balls & placing inside basket
array_of_balls = new Ball[number_of_balls];
for (int index=0; index<number_of_balls; index++)
{
array_of_balls[index] = new Ball();
}
}
//Drawing the balls and basket outline
void draw()
{
background (125,95,225);
for (int index=0; index<number_of_balls; index++)
{
array_of_balls[index].Draw();
}
noFill();
stroke(180,0,0);
strokeWeight(linethickness);
rect(basket_position.x, basket_position.y, basket_dimensions.x, basket_dimensions.y);
}
void mouseDragged()
{
if ((mouseX >= (ball_position.x - radius)) && (mouseX <= (ball_position.x + radius)) && (mouseY >= (ball_position.y - radius)) && (mouseY <= (ball_position.y + radius)))
{
ball_position = new PVector (mouseX, mouseY);
}
}
//Ball_class
int radius;
Ball()
{
radius = 10;
ball_position = new PVector (random(basket_position.x + radius + linethickness, basket_position.x + basket_dimensions.x - radius - linethickness), random(basket_position.y + radius + linethickness, basket_position.y + basket_dimensions.y - radius - linethickness));
colour = color(random(255), random(255), random(255));
}
void Draw()
{
noStroke();
fill(colour);
ellipse(ball_position.x,ball_position.y,radius*2,radius*2);
}
}
Thanks in advance for your help! I am using Processing 2.2.1 which I know is very out of date, so struggling to find help.
I have a piece of code that has created a number of balls, and I would like to be able to 'drag and drop' these to a different location on the screen as part of an educational game. I've tried playing around with mousePressed() and mouseDragged() but no luck yet. Any advice would be appreciated!

There are a lot of ways to approach this, but one way I could suggest is doing something like this:
// "Ellipse" object
function Ellipse (x, y, width, height) {
// Each Ellipse object has their own x, y, width, height, and "selected" values
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.selected = false;
// You can call the draw function whenever you want something done with the object
this.draw = function() {
// Draw ellipse
ellipse(this.x, this.y, this.width, this.height);
// Check if mouse is touching the ellipse using math
// https://www.desmos.com/calculator/7a9u1bpfvt
var xDistance = this.x - mouseX;
var yDistance = this.y - mouseY;
// Ellipse formula: (x^2)/a + (y^2)/b = r^2
// Assuming r = 1 and y = 0:
// 0 + (x^2)/a = 1 Substitute values
// ((width / 2)^2)/a = 1 x = width / 2 when y = 0
// a = (width / 2)^2 Move numbers around
// a = (width^2) / 4 Evaluate
var a = Math.pow(this.width, 2) / 4;
// Assuming r = 1 and x = 0:
// 0 + (y^2)/b = 1 Substitute values
// ((height / 2)^2)/b = 1 y = height / 2 when x = 0
// b = (height / 2)^2 Move numbers around
// b = (height^2) / 4 Evaluate
var b = Math.pow(this.height, 2) / 4;
// x^2
var x2 = Math.pow(xDistance, 2);
// y^2
var y2 = Math.pow(yDistance, 2);
// Check if coordinate is inside ellipse and mouse is pressed
if(x2 / a + y2 / b < 1 && mouseIsPressed) {
this.selected = true;
}
// If mouse is released, deselect the ellipse
if(!mouseIsPressed) {
this.selected = false;
}
// If selected, then move the ellipse
if(this.selected) {
// Moves ellipse with mouse
this.x += mouseX - pmouseX;
this.y += mouseY - pmouseY;
}
};
}
// New Ellipse object
var test = new Ellipse(100, 100, 90, 60);
draw = function() {
background(255);
// Do everything associated with that object
test.draw();
};
The math is a bit funky, and I might not be using the right version of Processing, but hopefully you found this at least slightly helpful :)

I'm kind of confused about what language you're using. Processing is a wrapper for Java, not JavaScript. Processing.js went up to version 1.6.6 and then was succeeded by p5.js. I'm going to assume you're using p5.js.
I don't know if this is a new thing in p5.js, but for easy, but not very user-friendly click-and-drag functionality I like to use the built-in variable mouseIsPressed.
If the ellipse coordinates are stored in an array of vectors, you might do something like this:
let balls = [];
let radius = 10;
function setup() {
createCanvas(400, 400);
for (let i = 0; i < 10; i++) {
balls.push(createVector(random(width), random(height)));
}
}
function draw() {
background(220);
for (let i = 0; i < balls.length && mouseIsPressed; i++) {
if (dist(mouseX, mouseY, balls[i].x, balls[i].y) < radius) {
balls[i] = createVector(mouseX, mouseY);
i = balls.length;
}
}
for (let i = 0; i < balls.length; i++) {
ellipse(balls[i].x, balls[i].y,
2 * radius, 2 * radius
);
}
}
This is the quickest way I could think of, but there are better ways to do it (at least, there are in p5.js). You could make a Ball class which has numbers for x, y, and radius, as well as a boolean for whether it's being dragged. In that class, you could make a method mouseOn() which detects whether the cursor is within the radius (if it's not a circle, you can use two radii: sq((this.x - mouseX)/r1) + sq((this.y - mouseY)/r2) < 1).
When the mouse is pressed, you can cycle through all the balls in the array of balls, and test each of them with mouseOn(), and set their drag boolean to true. When the mouse is released, you can set all of their drag booleans to false. Here's what it looks like in the current version of p5.js:
function mousePressed() {
for (let i = 0; i < balls.length; i++) {
balls[i].drag = balls[i].mouseOn();
if (balls[i].drag) {
i = balls.length;
}
}
}
function mouseReleased() {
for (let i = 0; i < balls.length; i++) {
balls[i].drag = false;
}
}
I hope this helps.

The way your code is right now doesn't work in the current version of Processing either, but it's a pretty quick fix. I'm going to show you a way to fix that, and hopefully it'll work in the earlier version.
Here's where I think the problem is: when you use mouseDragged(), you try to change ball_position, but you don't specify which ball's position. Here's one solution, changing the mouseDragged() block and the Ball class:
void mouseDragged() {
for (int i = 0; i < array_of_balls.length; i++) {
if ((mouseX > (array_of_balls[i].ball_position.x - array_of_balls[i].radius)) &&
(mouseX < (array_of_balls[i].ball_position.x + array_of_balls[i].radius)) &&
(mouseY > (array_of_balls[i].ball_position.y - array_of_balls[i].radius)) &&
(mouseY < (array_of_balls[i].ball_position.y + array_of_balls[i].radius))
) {
array_of_balls[i].ball_position = new PVector (mouseX, mouseY);
i = array_of_balls.length;
}
}
}
//Ball_class
class Ball {
int radius;
PVector ball_position;
color colour;
Ball() {
radius = 10;
ball_position = new PVector (random(basket_position.x + radius + linethickness, basket_position.x + basket_dimensions.x - radius - linethickness), random(basket_position.y + radius + linethickness, basket_position.y + basket_dimensions.y - radius - linethickness));
colour = color(random(255), random(255), random(255));
}
void Draw() {
noStroke();
fill(colour);
ellipse(ball_position.x, ball_position.y, radius*2, radius*2);
}
}
P.S. Since you're using a language based in Java, you should probably adhere to the finnicky parts of the language:
data types are very strict in Java. Avoid assigning anything that could possibly be a float to a variable that is declared as an int. For example, in your setup() block, you say bucket_1_correct = number_of_balls*bucket_1;. This might seem like not an issue, since number_of_balls*bucket_1 is always going to be a whole number. But since the computer rounds when saving bucket_1 = 1/6, multiplying it by 6 doesn't necessarily give a whole number. In this case, you can just use round(): bucket_1_correct = round(number_of_balls*bucket_1);
Regarding data types, you should always declare your variables with their data type. It's a little hard for me to tell, but it looks to me like you never declared ball_position or colour in your Ball class, and you never opened up the class with the typical class Ball {. This might have been a copy/paste error, though.

Related

SetPixel faster on mouse drag

Hi I'm creating a cleaning game but encountered a problem when I fast draw a straight line the line is broken but when I slow draw a straight line it works fine
Below is my code
private void Update()
{
if (Input.GetMouseButton(0))
{
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out RaycastHit hit))
{
Vector2 textureCoord = hit.textureCoord;
int pixelX = (int)(textureCoord.x * _templateDirtMask.width);
int pixelY = (int)(textureCoord.y * _templateDirtMask.height);
Vector2Int paintPixelPosition = new Vector2Int(pixelX, pixelY);
int paintPixelDistance = Mathf.Abs(paintPixelPosition.x - lastPaintPixelPosition.x) + Mathf.Abs(paintPixelPosition.y - lastPaintPixelPosition.y);
int maxPaintDistance = 7;
if (paintPixelDistance < maxPaintDistance)
{
return;
}
lastPaintPixelPosition = paintPixelPosition;
int pixelXOffset = pixelX - (_brush.width / 2);
int pixelYOffset = pixelY - (_brush.height / 2);
for (int x = 0; x < _brush.width; x++)
{
for (int y = 0; y < _brush.height; y++) {
Color pixelDirt = _brush.GetPixel(x, y);
Color pixelDirtMask = _templateDirtMask.GetPixel(pixelXOffset + x, pixelYOffset + y);
float removedAmount = pixelDirtMask.g - (pixelDirtMask.g * pixelDirt.g);
dirtAmount -= removedAmount;
_templateDirtMask.SetPixel(
pixelXOffset + x,
pixelYOffset + y,
new Color(0, pixelDirtMask.g * pixelDirt.g, 0)
);
}
}
_templateDirtMask.Apply();
}
}
}
Start Paint, and using the pen, try draw circles as fast as you can then look at the result:
Obviously, you didn't draw such straight lines with such clean direction change.
So, how is Paint able to cope up with such huge delta changes?
Interpolation
Some pseudo code:
on mouse down
get current mouse position
if last mouse position has been set
draw all the positions between last to current
use Bresenham algorithm for instance
save current mouse position to last mouse position
You could/should make your algo aware about pen size, with some simple math you can figure out the necessary step in evaluating points in the interpolation.
And don't use SetPixel, keep a copy of the texture pixels with GetPixels32 that you'll update and then upload it all at once using SetPixels32.

Cellular automata with compute shader & race condition problem

I'm trying to make a simple "falling sand" cellular automata, following these two guides:
https://www.youtube.com/watch?v=5Ka3tbbT-9E&t=29s
https://www.youtube.com/watch?v=VLZjd_Y1gJ8&t=62s
I'm using compute shader, along with Unity to calculate the position of next frame's particles.
#pragma kernel UpTex
#define AIR -1
#define SAND 0
struct blockInfo
{
int id;
int lifeTime;
float velocity;
float4 col;
int updated;
};
struct cellInf
{
int xPos;
int yPos;
};
RWStructuredBuffer<blockInfo> readBlock, writeBlock;
int sizeX, sizeY;
RWStructuredBuffer<cellInf> location;
int ToOneDi(int i, int j)
{
int newX = sizeY - 1 - j, newY = i;
return (sizeX * newX + (newY));
}
[numthreads(16,1,1)]
void UpTex (uint3 id : SV_DispatchThreadID)
{
int2 pos = int2(location[id.x].xPos, location[id.x].yPos);
int cur = ToOneDi(pos.x, pos.y);
blockInfo inf = readBlock[cur];
if (inf.updated == 1) {return; } //updated then skip
inf.updated = 1;
writeBlock[cur] = inf; //preemptive sets
if (pos.y == 0 || pos.x == 0 || pos.x == sizeX - 1) {return;} //base level
if (inf.id == SAND)
{
int left = ToOneDi(pos.x-1, pos.y-1);
int mid = ToOneDi(pos.x, pos.y-1);
int right = ToOneDi(pos.x+1, pos.y-1);
blockInfo leftBlock = readBlock[left];
blockInfo midBlock = readBlock[mid];
blockInfo rightBlock = readBlock[right];
//checking -> double check
if (midBlock.id == AIR)
{
writeBlock[mid] = inf;
writeBlock[cur] = midBlock;
}
else if (leftBlock.id == AIR)
{
writeBlock[left] = inf;
writeBlock[cur] = leftBlock;
}
else if (rightBlock.id == AIR)
{
writeBlock[right] = inf;
writeBlock[cur] = rightBlock;
}
else
{
writeBlock[cur] = inf;
}
}
//writeBlock[cur].col = float4(1, 0, 0, 1); //test
}
Currently I'm only simulating air and sand. readBlock is the previous frame info, writeBlock is current frame info (which would be utilized in another compute shader to apply to a texture and location just contained the 2D position of the cell to be updated.
With this code, it's clear that 2 sand block of the previous frame can cause a race condition by accessing the same cell (when falling into that cell), causing the color displayed to be changed.
I've tried to fix the problem by creating different groups to be processed linearly, and the cells in each groups separated enough to not cause a race condition to each other.
The new problem is, as the video shown, if I tried to move a particle (like sand) more than one block at a time (basically adding velocity to it), the grouping would have to be changed again. At some point it'd probably cause some form of diminishing returns if I have to create too many groups.
Any ideas how to fix the race conditions other than extra groupings?
Further note:
Both youtubers linked above and even the devs of the game that they use as reference (Noita - https://www.youtube.com/watch?v=prXuyMCgbTc) seems to agree that brute-force with a single thread, then using chunking and other techniques for optimization is just more bug-free and less headache.
The 2 recreations use C/Java, and the devs create their own custom engine with C++ (and probably OpenGL from what I can piece together) which from my experience already ran much faster. As such I haven't been able to find any other fix to my problem.
Links:
https://www.reddit.com/r/Games/comments/d7cqjz/comment/f0z8hu8/
https://www.reddit.com/r/gamedev/comments/d93op6/noita_pixel_simulation_any_tip_about_how_is_it/

unity : making a power meter for a shooting game

i am making a game where i require a power meter. i am using drag and shoot method, and have LineRenderer as my trajectory. basically when u click and drag on the screen the ball moves in the direction it’s s dragged (just like bow and arrow). i want to display a power meter too but some how not able to achieved it. i have few approaches but they don’t give what i want. can you please have look and suggest what am doing wrong. thank you
{
ChangeAni();
if (onGround && ballMoveable)
{
if (Input.GetMouseButtonDown(0))
{
startPoint = cam.ScreenToWorldPoint(Input.mousePosition);
startPoint.z = 2;
}
if (Input.GetMouseButton(0))
{
currentPoint = cam.ScreenToWorldPoint(Input.mousePosition);
currentPoint.z = 2;
tl.RenderLine(startPoint, currentPoint);
force = new Vector2(Mathf.Clamp(startPoint.x - currentPoint.x, minPower.x, maxPower.x), Mathf.Clamp(startPoint.y - currentPoint.y, minPower.y, maxPower.y));
float powerDisplayX = Mathf.Clamp(startPoint.x - currentPoint.x, 0, power);
float powerDisplayY = Mathf.Clamp(startPoint.y - currentPoint.y, 0, power);
powerDisplay = powerDisplayX + powerDisplayY;
Debug.Log(powerDisplay);
//powerDisplay = force.x + force.y * power;
//if (powerDisplay < 0)
//{
// powerDisplay = -powerDisplay;
//}
}
if (Input.GetMouseButtonUp(0))
{
endPoint = cam.ScreenToWorldPoint(Input.mousePosition);
endPoint.z = 2;
//force = new Vector2(Mathf.Clamp(startPoint.x - endPoint.x, minPower.x, maxPower.x), Mathf.Clamp(startPoint.y - endPoint.y, minPower.y, maxPower.y));
rb.AddForce(force * power, ForceMode2D.Impulse);
tl.EndLine();
Debug.Log(force * power);
}
}```
I think you refer to the powerDisplay part.
Note that you are clamping both vector components individually so you might get a vector bigger than expected (with a maximum magnitude of sqrt(power^2 + power^2) = 1.41 * power ;)
.. you might rather want to use Vector2.ClampMagnitude
Returns a copy of vector with its magnitude clamped to maxLength.
like
powerDisplay = Vector2.ClampMagnitude(startPoint - currentPoint, power);

Unity3D BezierCurve - Spline Walker Following AI?

I've been struggling to create a game with a competent (but fair) racing AI, and I have several constraints that I'm trying to meet. Here's the conditions in order:
1.) The AI logic and player controls BOTH share the same car controller to drive and turn, etc. The AI vehicle simply passes a clamped (-1 to 1) value depending on how far the car is trying to turn and the throttle is engaged. Both the AI and player share the boolean to brake, and drift turning with the SHIFT key
2.) The AI needs to make smart, informed decisions that the player would in applying the right amount of turn, when to coast, etc.
I originally used a plain waypoint increment system to have the AI continue lapping around the track depending on the waypoints. With the right wheel friction curve values (which I happened to luckily find online) this actually works OK. However, while I was struggling more with the wheel friction values especially, and still want to have a smoother path following and turning logic for the AI, it was suggested to me to use cubic Bezier splines. I found the Catlike Coding tutorial fascinating (I'm sure many of you know this, but here's the link below for anyone interested):
https://catlikecoding.com/unity/tutorials/curves-and-splines/
I then got the idea of using the Spline Walker from this tutorial to be the ONE waypoint for the AI, and effectively "tease" the car to follow the spline walker, like this video:
https://www.youtube.com/watch?v=UcA4K2rmX-U#action=share
However, here's my big problem - if I want to have my car FOLLOW the spline walker, while keeping the spline walker always in front, I have to ensure the "progress" that the spline walker follows is relative to the position of the following car, with the spline walker remaining a LITTLE in front so that the car doesn't decide to slow down and stop.
I've been looking for examples to do this, and I can't say I've been too successful.
Here's my current progress calculating code snippets - I'm trying to store distances between positions on the spline right now, via the old waypoint objects which happen to share the same transform coordinates:
private void Start()
{
Rigidbody rb = chasingCar.GetComponent<Rigidbody>();
if(path)
{
nodes = path.GetComponentsInChildren<Transform>();
}
Array.Resize(ref distances, nodes.Length-1);
for (int i = 0; i < nodes.Length-1; i++)
{
//start storing the distances between two successive waypoints on the spline
distances[i] = Vector3.Distance(nodes[i].position, nodes[i + 1].position);
totalDistance += distances[i];
}
Debug.Log("First distance value is " + distances[0] + " and overall distance est is " + totalDistance);
Debug.Log("Second distance value is " + distances[1] + " and overall distance est is " + totalDistance);
Debug.Log("Fifth distance value is " + distances[4] + " and overall distance est is " + totalDistance);
}
This is in the update function for the spline walker when the chasing car and it's old waypoint path have been provided to the spline:
Vector3 position;
if (chasingCar && path)
{
float distFromCar = Vector3.Distance(transform.position, chasingCar.transform.position);
Debug.Log("Distance from car " + distFromCar);
if(distFromCar < 35)
{
//get current spline waypoint
//int splineIdx = GetSplineIndex(progress);
int splineIdx = chasingCar.GetComponent<CarEngine>().GetCurrentNodeTarget();
//declare next spline waypoint
int splineIdxNext = splineIdx + 1;
if (path && splineIdxNext == (nodes.Length))
splineIdxNext = 0;
Debug.Log("Current splineIdx " + splineIdx);
//float currCarDistance = Vector3.Distance(chasingCar.transform.position, nodes[splineIdx].position);
float currCarDistance = SumSplineProgress(splineIdx);
float overallDistance = Vector3.Distance(nodes[splineIdx].position, nodes[splineIdxNext].position);
float currCarSplineProgress = currCarDistance / overallDistance;
float overallProgress = (currCarDistance) / (totalDistance);
progress = overallProgress;
}
else
{
progress += Time.deltaTime / duration;
}
Debug.Log("Chasing, current progress: " + progress);
position = spline.GetPoint(progress);
Finally, here's the functions I've tried to use to calculate the spline walker progress in the past:
int GetSplineIndex(float progress)
{
float curProgress = progress * (totalDistance);
Debug.Log("Current calculated progress " + curProgress);
return System.Convert.ToInt32(Mathf.Floor(curProgress));
}
float SumSplineProgress(int index)
{
float currTotalDistance = 0f;
for(int i = index; i > -1; i--)
{
currTotalDistance += distances[i];
}
return currTotalDistance;
}
I might just be making it harder on myself than I need to, but I'm just going to say, I'm legit stumped. I got close with having the spline waypoint jump ahead of the car MORE when there is more distance between the current start and end waypoint for the AI car, but that's still not what I'm trying to achieve.
Anyone have any particular suggestions here? Tips, nudges in the direction, and code would be fantastic. Thanks in advance!
UPDATE
Yes, I'm still working on this! There was some logic that I regarded as faulty in the previous spline calculation code - for instance, this:
float currCarSplineProgress = currCarDistance / overallDistance;
I've changed to this:
float currCarSplineProgress = (currCarDistance) / currSplineLength;
The idea of that part is to check the car's progress on the current curve it is traveling close to in the overall spline, and position the spline walker accordingly so that it jumps ahead to the next spline when needed. Here's the full updated code:
Vector3 position;
if (chasingCar && path)
{
float distFromCar = Vector3.Distance(transform.position, chasingCar.transform.position);
Debug.Log("Distance from car " + distFromCar);
if(distFromCar < 50)
{
//get current spline waypoint
//int splineIdx = GetSplineIndex(progress);
int splineIdx = chasingCar.GetComponent<CarEngine>().GetCurrentNodeTarget()-1;
//declare next spline waypoint
int splineIdxNext = splineIdx + 1;
if(splineIdx == -1)
{
splineIdx = nodes.Length - 2;
splineIdxNext = 0;
}
if (path && splineIdxNext == (nodes.Length))
splineIdxNext = 0;
Debug.Log("Current splineIdx " + splineIdx);
//float currCarDistance = GetConvertedDistance(chasingCar.transform.position, nodes[splineIdx].position);
float currCarDistance = Vector3.Distance(chasingCar.transform.position, nodes[splineIdx].position);
float restDistance = Vector3.Distance(chasingCar.transform.position, nodes[splineIdxNext].position);
//float currCarDistance = SumSplineProgress(splineIdx);
Debug.Log("currCarDistance " + currCarDistance);
//float currSplineLength = Vector3.Distance(nodes[splineIdx].position, nodes[splineIdxNext].position);
float currSplineLength = currCarDistance + restDistance;
float overallDistance = 0;
float nextOverallDist = 0f;
if(splineIdx != 0)
overallDistance = SumSplineProgress(splineIdx-1);
Debug.Log("overallDistance " + overallDistance);
float currCarSplineProgNext = 0f;
if (splineIdxNext != 1 && splineIdxNext != 0)
{
nextOverallDist = SumSplineProgress(splineIdxNext - 1);
currCarSplineProgNext = (currCarDistance) / nextOverallDist;
}
Debug.Log("currSplineLength " + currSplineLength);
float currCarSplineProgress = (currCarDistance) / currSplineLength;
float leading = 10f;
if (distFromCar < 20)
leading += 15f;
float overallProgress;
Debug.Log("currCarSplineProgress " + currCarSplineProgress);
if (currCarSplineProgress < .7f)
{
overallProgress = (currSplineLength + (currCarDistance * .3f)) / (totalDistance);
}
else
{
Debug.Log("Jumping to next waypoint...");
overallProgress = (nextOverallDist + (currCarDistance * .3f)) / (totalDistance);
}
Debug.Log("Overall progress " + overallProgress);
//if (overallProgress >= 1f)
// overallProgress = 0f;
progress = overallProgress;
}
else
{
progress += Time.deltaTime / duration;
}
Debug.Log("Chasing, current progress: " + progress);
position = spline.GetPoint(progress);
}
else
{
position = spline.GetPoint(progress);
}
transform.localPosition = position;
Yet, unexpected things STILL happen when the car makes enough progress - the spline walker will just suddenly jump to one of the previous sections!
Any insights?
You have the right idea for making the AI chase a target moving along the spline. I believe that's how most AI in racing games work.
To ensure the target is always ahead of the AI, set the target's position to the AI's position on the spline added by some value related to how fast the AI is moving.
I would suggest checking out the Unity Standard Assets package:
https://assetstore.unity.com/packages/essentials/asset-packs/standard-assets-for-unity-2017-3-32351
There is a car AI system in there that works by following a target that is moving along a spline.

How to manipulate a shaped area of terrain in runtime - Unity 3D

My game has a drawing tool - a looping line renderer that is used as a marker to manipulate an area of the terrain in the shape of the line. This all happens in runtime as soon as the player stops drawing the line.
So far I have managed to raise terrain verteces that match the coordinates of the line renderer's points, but I have difficulties with raising the points that fall inside the marker's shape. Here is an image describing what I currently have:
I tried using the "Polygon Fill Algorithm" (http://alienryderflex.com/polygon_fill/), but raising the terrain vertices one line at a time is too resourceful (even when the algorithm is narrowed to a rectangle that surrounds only the marked area). Also my marker's outline points have gaps between them, meaning I need to add a radius to the line that raises the terrain, but that might leave the result sloppy.
Maybe I should discard the drawing mechanism and use a mesh with a mesh collider as the marker?
Any ideas are appreciated on how to get the terrain manipulated in the exact shape as the marker.
Current code:
I used this script to create the line - the first and the last line points have the same coordinates.
The code used to manipulate the terrain manipulation is currently triggered when clicking a GUI button:
using System;
using System.Collections;
using UnityEngine;
public class changeTerrainHeight_lineMarker : MonoBehaviour
{
public Terrain TerrainMain;
public LineRenderer line;
void OnGUI()
{
//Get the terrain heightmap width and height.
int xRes = TerrainMain.terrainData.heightmapWidth;
int yRes = TerrainMain.terrainData.heightmapHeight;
//GetHeights - gets the heightmap points of the tarrain. Store them in array
float[,] heights = TerrainMain.terrainData.GetHeights(0, 0, xRes, yRes);
if (GUI.Button(new Rect(30, 30, 200, 30), "Line points"))
{
/* Set the positions to array "positions" */
Vector3[] positions = new Vector3[line.positionCount];
line.GetPositions(positions);
/* use this height to the affected terrain verteces */
float height = 0.05f;
for (int i = 0; i < line.positionCount; i++)
{
/* Assign height data */
heights[Mathf.RoundToInt(positions[i].z), Mathf.RoundToInt(positions[i].x)] = height;
}
//SetHeights to change the terrain height.
TerrainMain.terrainData.SetHeights(0, 0, heights);
}
}
}
Got to the solution thanks to Siim's personal help, and thanks to the article: How can I determine whether a 2D Point is within a Polygon?.
The end result is visualized here:
First the code, then the explanation:
using System;
using System.Collections;
using UnityEngine;
public class changeTerrainHeight_lineMarker : MonoBehaviour
{
public Terrain TerrainMain;
public LineRenderer line;
void OnGUI()
{
//Get the terrain heightmap width and height.
int xRes = TerrainMain.terrainData.heightmapWidth;
int yRes = TerrainMain.terrainData.heightmapHeight;
//GetHeights - gets the heightmap points of the tarrain. Store them in array
float[,] heights = TerrainMain.terrainData.GetHeights(0, 0, xRes, yRes);
//Trigger line area raiser
if (GUI.Button(new Rect(30, 30, 200, 30), "Line fill"))
{
/* Set the positions to array "positions" */
Vector3[] positions = new Vector3[line.positionCount];
line.GetPositions(positions);
float height = 0.10f; // define the height of the affected verteces of the terrain
/* Find the reactangle the shape is in! The sides of the rectangle are based on the most-top, -right, -bottom and -left vertex. */
float ftop = float.NegativeInfinity;
float fright = float.NegativeInfinity;
float fbottom = Mathf.Infinity;
float fleft = Mathf.Infinity;
for (int i = 0; i < line.positionCount; i++)
{
//find the outmost points
if (ftop < positions[i].z)
{
ftop = positions[i].z;
}
if (fright < positions[i].x)
{
fright = positions[i].x;
}
if (fbottom > positions[i].z)
{
fbottom = positions[i].z;
}
if (fleft > positions[i].x)
{
fleft = positions[i].x;
}
}
int top = Mathf.RoundToInt(ftop);
int right = Mathf.RoundToInt(fright);
int bottom = Mathf.RoundToInt(fbottom);
int left = Mathf.RoundToInt(fleft);
int terrainXmax = right - left; // the rightmost edge of the terrain
int terrainZmax = top - bottom; // the topmost edge of the terrain
float[,] shapeHeights = TerrainMain.terrainData.GetHeights(left, bottom, terrainXmax, terrainZmax);
Vector2 point; //Create a point Vector2 point to match the shape
/* Loop through all points in the rectangle surrounding the shape */
for (int i = 0; i < terrainZmax; i++)
{
point.y = i + bottom; //Add off set to the element so it matches the position of the line
for (int j = 0; j < terrainXmax; j++)
{
point.x = j + left; //Add off set to the element so it matches the position of the line
if (InsidePolygon(point, bottom))
{
shapeHeights[i, j] = height; // set the height value to the terrain vertex
}
}
}
//SetHeights to change the terrain height.
TerrainMain.terrainData.SetHeightsDelayLOD(left, bottom, shapeHeights);
TerrainMain.ApplyDelayedHeightmapModification();
}
}
//Checks if the given vertex is inside the the shape.
bool InsidePolygon(Vector2 p, int terrainZmax)
{
// Assign the points that define the outline of the shape
Vector3[] positions = new Vector3[line.positionCount];
line.GetPositions(positions);
int count = 0;
Vector2 p1, p2;
int n = positions.Length;
// Find the lines that define the shape
for (int i = 0; i < n; i++)
{
p1.y = positions[i].z;// - p.y;
p1.x = positions[i].x;// - p.x;
if (i != n - 1)
{
p2.y = positions[(i + 1)].z;// - p.y;
p2.x = positions[(i + 1)].x;// - p.x;
}
else
{
p2.y = positions[0].z;// - p.y;
p2.x = positions[0].x;// - p.x;
}
// check if the given point p intersects with the lines that form the outline of the shape.
if (LinesIntersect(p1, p2, p, terrainZmax))
{
count++;
}
}
// the point is inside the shape when the number of line intersections is an odd number
if (count % 2 == 1)
{
return true;
}
else
{
return false;
}
}
// Function that checks if two lines intersect with each other
bool LinesIntersect(Vector2 A, Vector2 B, Vector2 C, int terrainZmax)
{
Vector2 D = new Vector2(C.x, terrainZmax);
Vector2 CmP = new Vector2(C.x - A.x, C.y - A.y);
Vector2 r = new Vector2(B.x - A.x, B.y - A.y);
Vector2 s = new Vector2(D.x - C.x, D.y - C.y);
float CmPxr = CmP.x * r.y - CmP.y * r.x;
float CmPxs = CmP.x * s.y - CmP.y * s.x;
float rxs = r.x * s.y - r.y * s.x;
if (CmPxr == 0f)
{
// Lines are collinear, and so intersect if they have any overlap
return ((C.x - A.x < 0f) != (C.x - B.x < 0f))
|| ((C.y - A.y < 0f) != (C.y - B.y < 0f));
}
if (rxs == 0f)
return false; // Lines are parallel.
float rxsr = 1f / rxs;
float t = CmPxs * rxsr;
float u = CmPxr * rxsr;
return (t >= 0f) && (t <= 1f) && (u >= 0f) && (u <= 1f);
}
}
The used method is filling the shape one line at a time - "The Ray Casting method". It turns out that this method starts taking more resources only if the given shape as a lot of sides. (A side of the shape is a line that connects two points in the outline of the shape.)
When I posted this question, my Line Renderer had 134 points defining the line. This also means the shape has the same number of sides that needs to pass the ray cast check.
When I narrowed down the number of points to 42, the method got fast enough, and also the shape did not lose almost any detail.
Furthermore I am planning on using some methods to make the contours smoother, so the shape can be defined with even less points.
In short, you need these steps to get to the result:
Create the outline of the shape;
Find the 4 points that mark the bounding box around the shape;
Start ray casting the box;
Check the number of how many times the ray intersects with the sides of the shape. The points with the odd number are located inside the shape:
Assign your attributes to all of the points that were found in the shape.