Unity Gizmos for arraylist - c#

How can i apply Gizmos to parallel arrays of positions ?
The current code i have now is that, the Gizmos does its job.
But since its a loop, it flashes which means going through the loop and drawing an array using the method
Gizmos.DrawRay();
But i don't want it to go through the loop and draw a ray and then removing it and repeating the process with a new position.
I want it to draw a ray for every element in the array and have the ray fixed where it doesn't get rewritten.
So here is my current code.
Edit :
void Platform_Position_Scale_Generator(int i) {
posX[i] = Random.Range(minPosRange, maxPosRange + 1);
posY[i] = Random.Range(minPosRange, maxPosRange + 1);
posZ[i] = 0;
scaleX[i] = Random.Range(minScaleRange, maxScaleRange + 1);
scaleY[i] = 1;
scaleZ[i] = 1;
}
void Platform_Generator(int i) {
platformPrefabPosition[i].x = posX[i];
platformPrefabPosition[i].y = posY[i];
platformPrefabPosition[i].z = posZ[i];
Instantiate(platformPrefab, platformPrefabPosition[i], Quaternion.identity);
platformPrefab.transform.localScale = new Vector3(scaleX[i], 1, 1);
}
void OnDrawGizmos() {
if(numOfGeneratedPlatforms < numOfPlatformsToGenerate) {
Platform_Position_Scale_Generator(numOfGeneratedPlatforms);
Platform_Generator(numOfGeneratedPlatforms);
Gizmos.color = Color.blue;
Gizmos.DrawRay(new Vector3((posX[numOfGeneratedPlatforms]), (posY[numOfGeneratedPlatforms]), (posZ[numOfGeneratedPlatforms])), transform.TransformDirection(Vector3.up));
numOfGeneratedPlatforms++;
}
}

Unity as well as the SceneView need to redraw the whole area everytime something changes. There is no way to draw something that will stay. If you want it to stay you have to redraw it every time the SceneView is redrawn. Unlike at runtime, at edittime Unity only redraws a window when some event happened.
So if you want to have a line for each position you have to draw a line for each position in OnDrawGizmos, everytime it's called. The SceneView works the same as the GameView. However it uses an invisible camera to render the scene from the editors perspective. Each time it is redrawn the SceneView will be cleared and completely redrawn. During that process Unity will call your OnDrawGizmos method where you have to draw everything you want to be visible in the scene.
edit
To draw all lines for all elelments you just have to use a loop, like this:
void OnDrawGizmos()
{
Gizmos.color = Color.blue;
for(int i = 0; i < posX.Length; i++)
{
Gizmos.DrawRay(new Vector3(posX[i], posY[i], posZ[i]), transform.up);
}
}
This will draw a one unit long line from each point in your list upwards. As i said if you work with positions it's usually way easier to work with a single array and use Vector3 has type:
public Vector3[] positions;
Or if you have to add / remove items quite often, use a generic List:
public List<Vector3> positions
ps: To me it looks a bit strage that you want to draw a line upwards. If you wanted to draw a line from point to point, you have to do:
void OnDrawGizmos()
{
Gizmos.color = Color.blue;
// important to reduce the upper limit by one since we add 1 inside the loop
for(int i = 0; i < posX.Length-1; i++)
{
Gizmos.DrawLine(
new Vector3(posX[i], posY[i], posZ[i]),
new Vector3(posX[i+1], posY[i+1], posZ[i+1])
);
}
}

Related

OverlapBox returns null almost every time

I've tried to make a script to spawn objects at a random position without them colliding with one another. It doesn't work properly as the OverlapBox returns almost every time null even when it touches a square.
Here is the script:
var quadBoundaries = quad.GetComponent<MeshCollider>().bounds;
var squareRadius = new Vector2(1, 1);
foreach (var square in squaresToSpawn)
{
_isOverlapping = true;
while (_isOverlapping)
{
_spawnPoint = new Vector2(Random.Range(quadBoundaries.min.x + 1.5f, quadBoundaries.max.x - 1.5f),
Random.Range(quadBoundaries.min.y + 1.5f, quadBoundaries.max.y - 1.5f));
_collisionWithSquare = Physics2D.OverlapBox(_spawnPoint, squareRadius,
0, LayerMask.GetMask("Square Layer"));
if (_collisionWithSquare is null)
{
square.transform.position = _spawnPoint;
_isOverlapping = false;
}
}
}
The quadBoundaries are the boundaries of a quad I placed so the squares will randomly spawn in a limited space.
My understanding is that I am generating a random point in the quad boundaries and then I check if on that point a square of scale (1,1) will fit without touching any other thing that has a collider and is on the square layer. if it touches then I generate a new point until the collision is null so I can place the square at the designated position.
But a bunch of things that I don't understand are happening.
First, the squares are touching each other. Second, just a few specific squares are registering a collision but even those are being touched by other squares. Third, when I scale up the square radius (for example 10,10) I get a big split between the squares (shown in the picture bellow).
I must add that all squares have a collider, are all on the square layer and the quad is on a different layer.
Can anybody explain to me what I'm not getting here? Thanks a lot!
Before the answer I'd like to say, that such algorithm of spawning is very dangerous, because you can enteran infinity loop, when there will no be place for new square. Minimum, that you can do to make this code more safe is to add a retries count for finding a place to spawn. But I will leave it on your conscience.
To make this algorithm work, you should understand that all physics in Unity are updated in fixed update. So all operations, that you do with Phisycs or Phisics2D are working with the state of objects, that was performed in the last Pyhsics update. When you change the position of the object, physics core don't capture this changes instantly. As a workaround you can spawn each object in the fixed update separately. Like this:
public class Spawner : MonoBehaviour
{
[SerializeField] private GameObject[] _squaresToSpawn;
[SerializeField] private GameObject _quad;
// Start is called before the first frame update
void Start()
{
StartCoroutine(Spawn());
}
private IEnumerator Spawn()
{
var quadBoundaries = _quad.GetComponent<MeshCollider>().bounds;
var squareRadius = new Vector2(1, 1);
Vector2 spawnPoint;
foreach (var square in _squaresToSpawn)
{
yield return new WaitForFixedUpdate();
var isOverlapping = true;
var retriesCount = 10;
while (isOverlapping && retriesCount > 0)
{
spawnPoint = new Vector2(Random.Range(quadBoundaries.min.x + 1.5f, quadBoundaries.max.x - 1.5f),
Random.Range(quadBoundaries.min.y + 1.5f, quadBoundaries.max.y - 1.5f));
var hit = Physics2D.OverlapBox(spawnPoint, squareRadius,
0, LayerMask.GetMask("Square"));
if (hit is null)
{
square.transform.position = spawnPoint;
isOverlapping = false;
}
else
{
retriesCount--;
if (retriesCount == 0)
{
Debug.LogError("Can't find place to spawn the object!!");
}
}
}
}
}
}
But such code will have effect of continious spawning:
To make objects spawning properly within one frame. You should manualy store all spawned objects bounding boxes inside your code, and manualy check if your new spawn bounding box collide with previously spawned objects.

Animate drawing LineRenderer with many position

I have a LineRenderer with many positions and am try to animate the line from point a -> b, b ->c, etc...
First, I save all the positions of the line, then I reset the positionCount so that it is not visible at the start. But when I draw the lineRenderer in a loop, increasing positionCount over each iteration, and when drawing the next line starts, the previous line shakes a little and the width changes momentarily.
Here is code:
public float LineDrawSpeed;
void Start()
{
lineRenderer = GetComponent<LineRenderer>();
int lineCountInLineRenderer = lineRenderer.positionCount - 1;
var startPositions = new Vector3[lineRenderer.positionCount];
lineRenderer.GetPositions(startPositions);
lineRenderer.positionCount = 1;
StartCoroutine(LineDrawCoroutine(startPositions));
}
Here is coroutine:
IEnumerator LineDrawCoroutine(Vector3[] positions)
{
for (int i = 0; i < positions.Length - 1; i++)
{
if (lineRenderer.positionCount <= i + 1)
lineRenderer.positionCount++;
lineRenderer.SetPosition(i, positions[i]);
float timePass = 0f;
while (timePass < LineDrawSpeed)
{
var factor = timePass / LineDrawSpeed;
factor = Mathf.SmoothStep(0, 1, factor);
lineRenderer.SetPosition(i + 1, Vector3.Lerp(positions[i], positions[i + 1], factor));
timePass += Mathf.Min(Time.deltaTime, LineDrawSpeed - timePass);
yield return null;
}
}
}
The mechanic works well, but something is wrong with animation.
I found this width variation topic quite interesting.
As far as I checked, there two important points to take into account.
1.- LineRenderer render billboard lines.
https://docs.huihoo.com/unity/5.3/Documentation/en/Manual/class-LineRenderer.html
Billboards are 2D elements incrusted in a 3D world, whose orientation is automatically computed so that it always faces the camera. This explains width variations along with camera movement.
2.- If the camera is not moving, take into account that: As defined in the documentation, width attribute defines: "a width value and a curve value to control the width of your trail along its length.
The curve is sampled at each vertex, so its accuracy is limited by the number of vertices in your trail. The overall width of the trail is controlled by the width value."
https://docs.unity3d.com/Manual/class-LineRenderer.html
So as you are dinamically changing the vertexes of your line, the overall width of your trail might suffer changes.
So I think that your algorithim works fine, and that the width variations comes along with the line renderer component.
I'm pretty confident your issue lies in the fact that you are adding to the position count
over and over again. Not 100% sure though.
Regardless, below is working code for how to incrementally increase the length of the line. It's just an IEnumerator, but you can just call it in StartCoroutine and it should work
This will work for a straight line, but will take some adjustments for a curved line (but I think you could make it work if you wanted to).
To explain this a bit extra, instead of incrementing and increasing the positionCount, and then adding the vector, I simply set the positionCount to the desired amount of vectors and then incrementally fill each vector in with the desired position. When setting them all right off the bat, they default to 0,0,0, so just be sure this doesn't cause any issues for you.
Below is the code I use:
// Using IEnumerator to ensure that the line is drawn over a specific amount of time
private IEnumerator DrawLine()
{
float renderTime = 2f; // total time for full line render
int renderSteps = 75; // desired resolution of render (higher amt of steps creates smoother render)
float timeBetweenSteps = renderTime / renderSteps; // time between each render step
// Grab the LineRenderer component that is attached to the gameObject and defined in Inspector
LineRenderer lineRenderer = yourGameObject.GetComponent<LineRenderer>();
// Declare the endpoint
Vector3 endPoint = new Vector3(0, 5, 0);
// Take the end point and break into the amount of steps we need in order to create
// fully rendered line
// Create an additiveVector for the forLoop that will step through the renderSteps
// >> Start at zero becuase zero is the localOrigin
Vector3 stepVector = endPoint / renderSteps;
Vector3 additiveVector = Vector3.zero;
// Setting the line's position element count to the render resolution because we will
// have to step through
lineRenderer.positionCount = renderSteps;
// For Loop that steps through each position element in the Line
for (int i = 0; i != lineRenderer.positionCount; i++)
{
lineRenderer.SetPosition(i, additiveVector); // set the position element to the additiveVEctor
additiveVector += stepVector; // add one step to the additiveVector
yield return new WaitForSeconds(timeBetweenSteps); // Wait the appropriate step time before repeating the loop
}
}
Let me know if you have any questions. I hope this helps!

Mouse Drag - C# bot

First time doing this. I am currently building a bot using C# and want my bot to be able to move the mouse to a given point in a way that looks human. By this I am referring to the dragging of the mouse when a human moves the cursor to a point they are trying to click on. Currently my C# bot moves the mouse instantly to the location which doesn't look human.
private static Point[] FindColor(Color color)
{
int searchValue = color.ToArgb();
List<Point> result = new List<Point>();
using (Bitmap bmp = GetScreenShot())
{
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
if (searchValue.Equals(bmp.GetPixel(x, y).ToArgb()))
result.Add(new Point(x, y));
}
}
}
return result.ToArray();
}
// FUNCTIONS OCCUR BELOW
// Error message if program could not find bitmap within screenshot show error message
Color myRgbColor = new Color(); // Creates new colour called myRgbColor
myRgbColor = Color.FromArgb(51, 90, 9); // This colour equals the RGB value
Point[] points = FindColor(myRgbColor); // Create an array called points which list all the points found in the screen where the RgB value matches.
if (points.Length > 0)
{
Cursor.Position = points[2]; // Move mouse cursor to first point (Point 0)
Thread.Sleep(0200);
MouseClick();
}
if (points.Length == 0)
{
MessageBox.Show("No matches!"); // Return error
goto checkore;
}
You're going to want to use some kind of Timer with a callback, to move the mouse incrementally, step by step. As for the movement itself, you have a world of possibilities, but it's all maths.
So, let's decompose the problem.
What is a natural mouse movement?
Position change rate
It doesn't necessarilly looks like it, but when you move your mouse, you're simply setting its position multiple times per seconds.
The amount of times the position changes per second is equivalent to the polling rate of your mouse. The default polling rate for USB mice is 125Hz (or 125 position changes per second, if you will). This is the value we'll use for our Timer: its callback will be called 125 times per second.
var timer = new Timer(1000 / 125d);
timer.Elapsed += MoveMouse;
void MoveMouse(object sender, ElpasedEventArgs e) { }
Speed and acceleration
When you move your mouse, the distance between two cursor positions is not constant, because you're fast when you start moving your mouse, but you slow down when you get close to the item you want your cursor to be on.
There are also two ways I personally usually move my mouse depending on the context/mood:
One fast uniform movement to get close to the destination, then one slow to correct and get on it (I'll usually go past the destination during the first move)
One medium-slow movement with a small deceleration, follow by a stronger deceleration at the end
The overall speed of the movement also depends on three factors:
The distance between your cursor and the destination
The size of the destination area
Your personal speed
I have absolutely NO IDEA how to work out the formula based on these factors, that's gonna be a work of trial and error for yourself.
This one is purely math and observation based, and will be tricky to get perfectly right, if ever; every person moves their mouse a different way.
The solution I can offer you is to simply forget about deceleration, correction and so on, and just divide your movement into equal steps. That has the merit of being simple.
using System;
using System.Timers;
using System.Drawing;
public class Program
{
static int stepCount = 0;
static int numberOfSteps = 0;
static float stepDistanceX = 0;
static float stepDistanceY = 0;
static PointF destinationPoint;
static Timer timer;
public static void Main()
{
int timerStepDurationMs = 1000 / 125;
PointF currentPoint = Cursor.Position;
destinationPoint = new PointF(2000, 1800); // or however you select your point
int movementDurationMs = new Random().Next(900, 1100); // roughly 1 second
int numberOfSteps = movementDurationMs / timerStepDurationMs;
stepDistanceX = (destinationPoint.X - currentPoint.X) / (float)numberOfSteps;
stepDistanceY = (destinationPoint.Y - currentPoint.Y) / (float)numberOfSteps;
timer = new Timer(timerStepDurationMs);
timer.Elapsed += MoveMouse;
timer.Start();
while (stepCount != numberOfSteps) { }
}
static void MoveMouse(object sender, ElapsedEventArgs e)
{
stepCount++;
if (stepCount == numberOfSteps)
{
Cursor.Position = destinationPoint;
timer.Stop();
}
Cursor.Position.X += stepDistanceX;
Cursor.Position.Y += stepDistanceY;
}
}
Note that I haven't tested with "Cursor", but with some PointF variable instead. It seems to work fine here: dotnetfiddle.

Unity3D/NGUI UI elements drawn at wrong location

I have a curious problem with rendering the actual content in NGUI widgets when the underlying panel is moved. For some reason, the content itself doesn't update to correct position even if the bounds are. First image is how it should be and second is how it is sometimes after moving the underlying panel from right to left. You can see from the widget rectangles that they are in correct place but the contents (text or sprite) is misplaced.
I have tried updating, refreshing anchors etc. but none seem to be working. Any ideas? It seems to be a rendering problem. We are using Unity 4.6 and NGUI 3.7.4 currently.
I suppose NGUI don't work well with (parent) gameobject being inactive.
Especially if you try to UIScrollView.ResetPosition, scaling or re-positioning.
so I have an extension that look like this
public static void ResetPositionHack(this UIScrollView scrollView, float animLength = 1f ) {
scrollView.ResetPosition();
scrollView.currentMomentum = Vector3.zero;
KeepMakingDirty.Begin( scrollView, animLength );
}
The method KeepMakingDirty.Begin will attach a MonoBehavior to the object (also make sure no double attachment).
SetDirty() will be called in it's Update() for a specified period of time.
Then the script self-destructs.
This is a NGUI's bug. It is related to UIPanel.UpdateTransformMatrix(), and has been fixed at NGUI3.9.3 or NGUI 3.9.4.
The code blow works fine in my project:
void UpdateTransformMatrix ()
{
int fc = Time.frameCount;
if (cachedTransform.hasChanged)
{
mTrans.hasChanged = false;
mMatrixFrame = -1;
}
if (mMatrixFrame != fc)
{
mMatrixFrame = fc;
worldToLocal = mTrans.worldToLocalMatrix;
Vector2 size = GetViewSize() * 0.5f;
float x = mClipOffset.x + mClipRange.x;
float y = mClipOffset.y + mClipRange.y;
mMin.x = x - size.x;
mMin.y = y - size.y;
mMax.x = x + size.x;
mMax.y = y + size.y;
}
}

How to detect if player intersects PART of the world

I'm sorry if question title was unclear, but with my cheap english, I cant find a way to ask it clearly.
But I can explain it in long way.
So I have realized if I design my world(and with world, I mean ENTIRE game, it will be one level) 10.000x10.000... it will be very enough, other than few another sprite(and I mean like 4 or 5 with maximum of 50x50, nothing big.)
So I thought, why dont I make my entire map as 10.000x10.000(or lets say tons of 512x512) picture ?
But I have one question, there is few things you can "interact". they will(with they, I mean the stuff that is in my "world.jpg") be always stay at same place, but player(which is actually a sprite as you know) will move, therefore my 10.000x10.000 will "move".
So look at picture below, there is black dot which is "player" and red dot, which is lets say, a door.
and world is always centered to black dot unless he goes to end of the world. as you can see, (look at picture part 1 and part 2) when he moves a little bit to east, red dot looks moved. but I just moved my 10.000x10.000 image. Thats what I meant with the stuff on 10kx10k pic will move.
Anyway, but as you can see in last part of pic, when he goes near red dot, I want to my "action"
How to do it ?
-part below is not really related to main question
Is it useful to use 10kx10 pic instead of another sprites appearing on world when he moves ? but If I want to do that, not just I will check if he is nearby, but I will also check his point to realize if I should or shouldnt show him sprite.
Will it be more useful if I show my stuff when he comes to coordinates I want, or is using one big picture is OK ?
Thanks.
I would suggest a structure of the map somewhat like this..
public class Map
{
public MapPoint[,] mapPoints; //the map
public Player player; //the player/user object
public Vector2 DrawHeroPosition;
//where at the screen the player is going to be drawn
public Vector2 RangeStart;
//what part of the map who is going to be drawn
public int SizeX; //number of mapPoints the screen can contain at one time
public int SizeY; //number of mapPoints the screen can contain at one time
//MapPoint represents a specific 512x512 point (mapPoint) its position at
//the map but also includes the sprite that is going to be drawn and objects
//that the player can interact with at that place (like the door)
//the player object includes reference to where in the world it is place
public Map(ContentManager theContentManager, int x, int y)
{
MapSizeX = x;
MapSizeY = y;
int ScreenSizeX = 9;
int ScreenSizeY = 9;
mapPoints = new MapPoint[MapSizeX , MapSizeY];
//ad code for generating/creating map...
//important that you store the MapPoints position inside each mapPoint
player = new Player(mapPoints[0,0]); //crate a player who knows where he is
}
public void Update()
{
//in the update method you do a lot of things like movement and so
//set what part of the map the game should draw if the game for example
//can show 9x9 512points at a single time
//give range value from the players position
RangeStart.X = player.PositionX;
//test if the maps position is in the left corner of the map
//if it is draw the map from the start..(RangeStart.X = 0)
if (player.PositionX - (ScreenSizeX / 2) < 0) { RangeStart.X = 0; }
//if not draw the hero in the mitle of the screen
else
{
RangeStart.X = player.PositionX - (ScreenSizeX / 2);
}
//if the hero is in the right corer, fix his position
while (RangeStart.X + ScreenSizeX > MapSizeX)
{
RangeStart.X--;
}
//the same thing for the Y axle
RangeStart.Y = player.PositionY;
if (player.PositionY - (ScreenSizeY / 2) < 0) { RangeStart.Y = 0; }
else
{
RangeStart.Y = player.PositionY - (ScreenSizeY / 2);
}
while (RangeStart.Y + ScreenSizeY > MapSizeY)
{
RangeStart.Y--;
}
//time to set the position of the hero...
//he works like the opposite of the range, if you move what part of the map
//you draw you dont change the heros draw position, if you dont move the range
//you have to move the hero to create the illusion of "moment"
//if you are in the left part you have to move the heros draw position..
if (player.PositionX - (ScreenSizeX / 2) < 0)
{ DrawHeroPosition.X = player.PositionX; }
//if you are in the right part
else if (player.PositionX+1 > MapSizeX - (ScreenSizeX / 2))
{
DrawHeroPosition.X = player.PositionX - (MapSizeX - ScreenSizeX);
}
//if you aint in a corner, just place the hero in the middle of the map
else
{
DrawHeroPosition.X = (ScreenSizeX / 2);
}
//the same thing for Y
if (player.PositionY - (ScreenSizeY / 2) < 0)
{ DrawHeroPosition.Y = player.PositionY; }
else if (player.PositionY+1 > MapSizeY - (ScreenSizeY / 2))
{
DrawHeroPosition.Y = player.PositionY - (MapSizeY - ScreenSizeY);
}
else
{
DrawHeroPosition.Y = (ScreenSizeY / 2);
}
}
public void Draw()
{
int x = (int)RangeStart.X;
int y = (int)RangeStart.Y;
for(int counterX = 0; x < ((MapSizeX)); x++, counterX++)
{
for (int counterY = 0; y < (MapSizeY); y++, counterY++)
{
if (mapPoints[x, y] != null)
{
mapPoints[x, y].Draw(spriteBatch, mapPoints[counterX,counterY].positonInMatrix);
//mapPoints[counterX,counterY] = where to draw
//mapPoints[x, y] = what to draw
}
}
y = (int)RangeStart.Y;
}
}
}
how i draw inside the MapPoint Class...
public void Draw(SpriteBatch theSpriteBatch, Vector2 positonOnScreen)
{
positonOnScreen = new Vector2(positonOnScreen.X * base.Scale * 16,
positonOnScreen.Y * base.Scale * 16);
//base.Scale is just a variable for have the ability to zoom in/out
//16 represents the original size of the picture (16x16 pixels)
theSpriteBatch.Draw(mSpriteTexture, new Rectangle((int)positonOnScreen.X,
(int)(positonOnScreen.Y), 64, 64),new Rectangle(0, 0, 16, 16), Color.White);
}
If you are asking for collision detection within a radius of your red dot. You can simply use the following test (pseudocode, I don't write C# :-)
if( (player.GetPosition() - point.GetPosition()).length() < radius )
{ /* Do code here */ }
This will detect if your player is within a certain radius of your dot, you can then perform whatever action you wish.
Hope this helps! :)
Ok, from what I understand of your question, you have a large image which contains different objects you want your player to interact with, yes? By which I mean, the image file itself has doors or hills or other things which the player would interact with.
This is a bad idea, honestly, so I hope I misunderstood. It is far better to have your background image just be something generic and make all interactive objects classes within your game. If you do this, then you can have your object classes contain behavior to intersect with each other either based on their distance (circle collision) or based on a bounding box you define for them.
Circle Collision:
if (Math.Abs(Vector2.Distance(player.Position - obj.Position)) < player.Radius + obj.Radius)
//do the action
Rectangle Collision:
if (player.Bounds.Intersects(obj.Bounds))
//do the action
Also, if you are planning on making a 10,000 x 10,000 pixel image, understand that the XNA Content Pipeline will not import an image greater than 4,000 pixels to a side.
If you are set on having the player interact with pixels in the background of the image, you can either make an array of interactive object locations manually or you can use the Texture2D.GetData() method to load in the colors of every single pixel in the image for processing-- but be aware that this will take a long time, especially for large or numerous textures.

Categories

Resources