How can I scale a unity sprite programmatically to a fractional number of units? - unity3d

I've spawned a tile as follows:
private GameObject SpawnTile(int col, int row, Color color, GameObject parent, string label)
{
GameObject g = new GameObject("C: " + col + " R: " + row);
g.transform.position = world_grid.GetWorldPosition(col, row);
g.transform.localScale = new Vector3(world_grid.cell_size, world_grid.cell_size);
g.transform.parent = parent.transform;
return g;
}
I then add a spriterenderer, and display a sprite of a given color.
tile.game_object = SpawnTile(col, row, color, parent, label);
tile.sprite_renderer = tile.game_object.AddComponent<SpriteRenderer>();
tile.sprite_renderer.sprite = tile_sprite;
//Bounds bounds = tile.sprite_renderer.bounds;
tile.sprite_renderer.color = color;
I have the ability to display grids using this mechanism. I can scale the size of a tile up by an integer amount. However when I try to scale the object down, the sprite does not scale down with it.
In the following, I have 3 1x1 grids. The grid on the right has a cell_size of 2. The grid in the middle has a cell_size of 1. The grid on the left has a cell_size of 0.5.
The "tile" object is of an appropriate size. As we see this object is shown as half size in Unity's Scene View. However in the game view on the right, it shows as the same size as the pink grid in the middle.
Further details, I have spent quite a bit of time trying to figure this out, and though I see a variety of posts that seem to relate to sprite sizing, none of them are clear for a relative unity beginner.
Some of the posts I've found seem to suggest that I need to use the sprite renderer bounds to ensure that the sprite is proeprly sized. But its not clear to me how.
This is my grid class. Notice that it is constructed with a cell_size (in units). Notice that when I create the object of a given size, I size the object, but not the sprite.
public class WorldGrid<TGridObject> : Grid<TGridObject>
{
public float cell_size { get; private set; }
// Defines the center point in world coordinates
public Vector3 center_point { get; private set; }
public WorldGrid(Vector3 _center_point, int _columns, int _rows, float _cell_size) : base(_columns, _rows)
{
cell_size = _cell_size;
center_point = _center_point;
}
public Vector3 GetWorldPosition(int col, int row)
{
float x_pos = col * cell_size + cell_size / 2f;
float y_pos = row * cell_size + cell_size / 2f;
// wp = cp + gp
return center_point + new Vector3(x_pos, y_pos);
}
public void GetGridPosition(Vector3 world_position, out int col, out int row)
{
// gp = wp - cp
// position in grid-centered reference frame
Vector3 gp = world_position - center_point;
row = Mathf.FloorToInt(gp.y / cell_size);
col = Mathf.FloorToInt(gp.x / cell_size);
}
public void SetValue(Vector3 world_position, TGridObject value)
{
int row, col;
GetGridPosition(world_position, out row, out col);
SetValue(row, col, value);
}
}
This references a lower level grid class (that is not in world units):
public class Grid<TGridObject>
{
public int columns { get; private set; }
public int rows { get; private set; }
public TGridObject[,] grid { get; private set; }
private int cnt = 0;
public event EventHandler<OnGridCellValueChangedEventArgs> OnGridCellValueChanged;
public class OnGridCellValueChangedEventArgs : EventArgs
{
public int cnt;
public int row;
public int column;
}
public Grid(int _columns, int _rows)
{
columns = _columns;
rows = _rows;
grid = new TGridObject[columns, rows];
}
public void SetValue(int col, int row, TGridObject value)
{
if((row >= 0 && row < rows) &&
(col >= 0 && col < columns))
{
grid[col, row] = value;
if(OnGridCellValueChanged != null)
{
cnt += 1;
Debug.LogError("Triggering [" + col + "," + row + "] of [" + columns + "," + rows + "]");
OnGridCellValueChanged(this,
new OnGridCellValueChangedEventArgs { row = row, column = col, cnt = cnt }
);
}
}
}
public TGridObject GetValue(int col, int row)
{
if ((row >= 0 && row < rows) &&
(col >= 0 && col < columns))
{
return grid[col, row];
} else
{
return default(TGridObject);
}
}
}
I instantiate grids using a GridManager class:
public class GridManager : MonoBehaviour
{
[System.Serializable]
private struct WorldGridDescriptor
{
public int rows;
public int columns;
public float cell_size;
public Vector3 center_point;
public bool randomize_start;
}
public Sprite tile_sprite;
public int world_size_factor = 1;
private int columns, rows;
private float unit_step_size = 1.0f;
private List<WorldGrid<float>> world_grids = new List<WorldGrid<float>>();
private List<GridView> world_grid_views = new List<GridView>();
[SerializeField] private List<WorldGridDescriptor> world_grid_descriptors;
// Start is called before the first frame update
void Start()
{
// The orthographic size specifies the camera units from
// the horizontal centerline to the top of screen
// The vertical_units are then the number of tile rows
// from the centerline to the top of screen
rows = (int)Camera.main.orthographicSize * world_size_factor * 2;
float aspect_ratio = ((float)Screen.width / (float)Screen.height);
columns = (int)(rows * aspect_ratio);
// Will parent the tiles in the empty Grid.
// Perhaps this container should be public.
// For now it is a hard-coded assumption, that it exists.
GameObject grid_container = this.gameObject.transform.Find("Grid").gameObject;
if (world_grid_descriptors != null)
{
foreach (WorldGridDescriptor wgd in world_grid_descriptors)
{
WorldGrid<float> wg = new WorldGrid<float>(wgd.center_point, wgd.columns, wgd.rows, wgd.cell_size);
if(wgd.randomize_start)
{
RandomizeState(wg);
}
GridView grid_view = new GridView(wg, tile_sprite, grid_container);
world_grids.Add(wg);
world_grid_views.Add(grid_view);
}
}
}
private void RandomizeState(WorldGrid<float> g)
{
for (int col = 0; col < g.columns; col++)
{
for (int row = 0; row < g.rows; row++)
{
g.SetValue(col, row, Random.Range(0.0f, 1.0f));
}
}
}
private void Update()
{
if (Input.GetMouseButtonDown(0)) {
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
foreach (WorldGrid<float> wg in world_grids)
{
int c, r;
wg.GetGridPosition(worldPosition, out c, out r);
wg.SetValue(c, r, Random.Range(0.0f, 1.0f));
}
}
}
}
Lastly, the view into the grids are created as follows (this shows you exactly how I create the grid representation that uses a sprite renderer component to render sprites for each cell in the grid):
public class GridView
{
public struct Tile
{
public GameObject game_object;
public SpriteRenderer sprite_renderer;
public TextMesh text_mesh;
}
private Tile[,] tiles;
private WorldGrid<float> world_grid;
protected Sprite tile_sprite { get; private set; }
public GridView(WorldGrid<float> wg, Sprite ts, GameObject parent)
{
world_grid = wg;
tile_sprite = ts;
tiles = new Tile[wg.columns, wg.rows];
wg.OnGridCellValueChanged += OnGridCellValueChanged;
Create(parent);
}
private void Create(GameObject parent)
{
for (int col = 0; col < world_grid.columns; col++)
{
for (int row = 0; row < world_grid.rows; row++)
{
Tile tile = new Tile();
float r, g, b;
r = g = b = world_grid.grid[col, row];
float a = 1f;
Color color = new Color(r, g, b, a);
string label = (string)((int)(255 * world_grid.grid[col, row])).ToString("x");
tile.game_object = SpawnTile(col, row, color, parent, label);
tile.sprite_renderer = tile.game_object.AddComponent<SpriteRenderer>();
tile.sprite_renderer.sprite = tile_sprite;
//Bounds bounds = tile.sprite_renderer.bounds;
tile.sprite_renderer.color = color;
tile.text_mesh = UtilsClass.CreateWorldText(label, parent.transform,
tile.game_object.transform.position, 7, Color.red, TextAnchor.MiddleCenter);
tiles[col, row] = tile;
}
}
}
// Update is called once per frame
private GameObject SpawnTile(int col, int row, Color color, GameObject parent, string label)
{
GameObject g = new GameObject("C: " + col + " R: " + row);
g.transform.position = world_grid.GetWorldPosition(col, row);
g.transform.localScale = new Vector3(world_grid.cell_size, world_grid.cell_size);
g.transform.parent = parent.transform;
return g;
}
private void OnGridCellValueChanged(object sender, Grid<float>.OnGridCellValueChangedEventArgs e)
{
Debug.LogError(e.cnt + " - Updating [" + e.column + "," + e.row + "]");
Update(e.row, e.column);
}
private void Update(int row, int col)
{
if (row >= 0 && row < world_grid.rows && col >= 0 && col < world_grid.columns)
{
Tile tile = tiles[col, row];
float r, g, b;
r = g = b = world_grid.grid[col, row];
float a = 1f;
Color color = new Color(r, g, b, a);
string label = (string)((int)(255 * world_grid.grid[col, row])).ToString("x");
tile.text_mesh.text = label;
tile.sprite_renderer.color = color;
}
}
}
My sprites are a simple blank image that is 256 x 256 pixels in size. I then set the pixels per unit (in the unity editor) to 256.
How can I scale the sprite so that it aligns properly with the spawned tile?

Looks like my only issue was in the Display setting.
I had been using a 10x10 display.
When I changed it to something more reasonable, I realized that my gameobjects were being displayed and selected as expected.

Related

Graphics.DrawMesh has wired offset while scaling in Unity, why?

I'm implementing after image effect currently and I meet a problem with Graphics.DrawMesh. The code shows below
public class AfterImage3DByCombine : MonoBehaviour
{
public class AfterImange
{
public Mesh mesh;
public Material material;
// public Matrix4x4 matrix;
public float duration;
public float time;
}
protected SkinnedMeshRenderer[] skinRenderers;
protected MeshFilter[] filters;
protected int filtersCount = 0;
public bool IncludeMeshFilter = true;
public Material EffectMaterial;
public float Duration = 5;
public float Interval = 0.2f;
public float FadeoutTime = 1;
private float mTime = 5;
private List<AfterImange> mAfterImageList = new List<AfterImange>();
protected virtual void Awake()
{
skinRenderers = GetComponentsInChildren<SkinnedMeshRenderer>();
if (IncludeMeshFilter)
{
filters = GetComponentsInChildren<MeshFilter>();
filtersCount = filters.Length;
}
}
//call from another place to have after image effect
public void Play()
{
if (skinRenderers.Length + filtersCount <= 0)
{
return;
}
mTime = Duration;
StartCoroutine(AddAfterImage());
}
IEnumerator AddAfterImage()
{
while (mTime > 0)
{
CreateImage();
yield return new WaitForSeconds(Interval);
mTime -= Interval;
}
yield return null;
}
void CreateImage()
{
CombineInstance[] combineInstances = new CombineInstance[skinRenderers.Length + filtersCount];
int index = 0;
for (int i = 0; i < skinRenderers.Length; i++)
{
var render = skinRenderers[i];
var mesh = new Mesh();
render.BakeMesh(mesh);
combineInstances[index] = new CombineInstance
{
mesh = mesh,
transform = render.gameObject.transform.localToWorldMatrix,
subMeshIndex = 0
};
index++;
}
for (int i = 0; i < filtersCount; i++)
{
var render = filters[i];
var temp = (render.sharedMesh != null) ? render.sharedMesh : render.mesh;
var mesh = (Mesh)Instantiate(temp);
combineInstances[index] = new CombineInstance
{
mesh = mesh,
transform = render.gameObject.transform.localToWorldMatrix,
subMeshIndex = 0
};
index++;
}
Mesh combinedMesh = new Mesh();
combinedMesh.CombineMeshes(combineInstances, true, true);
mAfterImageList.Add(new AfterImange
{
mesh = combinedMesh,
material = new Material(EffectMaterial),
time = FadeoutTime,
duration = FadeoutTime,
});
}
void LateUpdate()
{
bool needRemove = false;
foreach (var image in mAfterImageList)
{
image.time -= Time.deltaTime;
if (image.material.HasProperty("_Color"))
{
Color color = Color.red;
color.a = Mathf.Max(0, image.time / image.duration);
image.material.SetColor("_Color", color);
}
Matrix4x4 mat = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, Vector3.one * 2f);
//public static void DrawMesh(Mesh mesh, Matrix4x4 matrix, Material material, int layer, Camera camera, int submeshIndex, MaterialPropertyBlock properties, ShadowCastingMode castShadows);
Graphics.DrawMesh(image.mesh, Matrix4x4.identity, image.material, gameObject.layer, null, 0, null, false);
if (image.time <= 0)
{
needRemove = true;
}
}
if (needRemove)
{
mAfterImageList.RemoveAll(x => x.time <= 0);
}
}
}
Since my prefab has 0.5 times scaling while it's running, then I pass a matrix with two times scaling Matrix4x4 mat = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, Vector3.one * 2f); into
Graphics.DrawMesh.
Then, the mesh created by Graphics.DrawMesh isn't in its original position, there is an offset between original mesh and created mesh.
And if I passed Matrix4x4.Identity into Graphics.DrawMesh, the created mesh will have 0.5 times scaling, which looks smaller than original mesh.
Why there is an offset and how could I eliminate the offset without chaning the prefab's scale?

How to display text in Unity game

I inherited by my ex collegue a Unity game. Now I need to implement this behaviour. The game consist in a car drive by user using Logitech steering.
Now I need to show a Text every X minute in a part of the screen like "What level of anxiety do you have ? " Ad the user should to set a value from 0 to 9 using the Logitech steering but I really don't know where to start.
This is my manager class:
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
namespace DrivingTest
{
// Braking test Manager
public class BtManager : BaseTestManger
{
// Public variables
public BreakCar breakCar;
public float minBrakeThreshold = 0.2f;
internal int vehicalSpeed;
internal int noOfEvents;
internal float durationOfTest;
// Private variables
private IRDSPlayerControls playerControlCar;
protected int xSpeed;
protected int ySpeed;
private float brakeEventStartTime;
private float brakeEventDifferenceTime;
private SafetyDistanceCheck safetyDistanceCheck;
protected float totalSafetyDistance;
protected int totalSafetyDistanceCount;
private List<float> averageEventReactionTime = new List<float>();
#region Init
// Use this for initialization
public override void Start()
{
base.Start();
playerControlCar = car.GetComponent<IRDSPlayerControls>();
safetyDistanceCheck = car.GetComponent<SafetyDistanceCheck>();
}
public override void OnEnable()
{
base.OnEnable();
BreakCar.BrakeStart += BrakeCarEvent;
BreakCar.BrakeEventEnd += CallTestOver;
}
public override void OnDisable()
{
base.OnDisable();
BreakCar.BrakeStart -= BrakeCarEvent;
BreakCar.BrakeEventEnd -= CallTestOver;
}
protected override void SetUpCar()
{
car.AddComponent<SelfDriving>();
}
#endregion
/// <summary>
/// Calls the main Test over method.
/// </summary>
private void CallTestOver()
{
GameManager.Instance.Testover();
}
protected override void OnSceneLoaded(eTESTS test)
{
UiManager.Instance.UpdateInstructions(Constants.BT_INSTRUCTION);
}
protected override void GetApiParams()
{
NextTestOutput nextTestOutput = GameManager.Instance.currentTest;
if (nextTestOutput.content != null && nextTestOutput.content.Count > 0)
{
foreach (var item in nextTestOutput.content[0].listInputParameter)
{
switch ((ePARAMETERS)item.id)
{
case ePARAMETERS.X_SPEED:
xSpeed = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.Y_SPEED:
ySpeed = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.SPEED_OF_VEHICLE:
vehicalSpeed = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.NUMBER_OF_EVENTS:
noOfEvents = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.DURATION_OF_TEST:
durationOfTest = float.Parse(item.Value) * 60; //converting minutes into seconds
break;
}
}
SetupBrakeCar();
}
}
protected virtual void SetupBrakeCar()
{
DOTween.Clear();
durationOfTest = noOfEvents * Random.Range(10,20); // The random value is the time between two consecutive tests.
breakCar.SetInitalParams(new BrakeCarInit
{
carMaxSpeed = vehicalSpeed * 0.95f,
durationOfTest = durationOfTest,
noOfEvents = noOfEvents,
xSpeed = xSpeed,
ySpeed = ySpeed
});
breakCar.distanceCheck = true;
}
protected override void SubmitRestultToServer()
{
SubmitTest submitTest = new SubmitTest
{
outputParameters = new List<SaveOutputParameter>
{
new SaveOutputParameter
{
id = (int)ePARAMETERS.OUT_REACTION_TIME,
value = ((int)(GetEventReactionTimeAvg () * Constants.FLOAT_TO_INT_MULTIPLAYER)).ToString()
},
new SaveOutputParameter
{
id = (int)ePARAMETERS.OUT_AVG_RT,
value = GetReactionResult().ToString()
}
}
};
WebService.Instance.SendRequest(Constants.API_URL_FIRST_SAVE_TEST_RESULT, JsonUtility.ToJson(submitTest), SubmitedResultCallback);
}
protected override void ShowResult()
{
UiManager.Instance.UpdateStats(
(
"1. " + Constants.EVENT_REACTION + (GetEventReactionTimeAvg() * Constants.FLOAT_TO_INT_MULTIPLAYER).ToString("0.00") + "\n" +
"2. " + Constants.REACTION_TIME + reactionTest.GetReactionTimeAvg().ToString("0.00")
));
}
public void LateUpdate()
{
//Debug.Log("TH " + .Car.ThrottleInput1 + " TH " + playerControlCar.Car.ThrottleInput + " brake " + playerControlCar.Car.Brake + " brake IP " + playerControlCar.Car.BrakeInput);
//Debug.Log("BrakeCarEvent "+ brakeEventStartTime + " Player car " + playerControlCar.ThrottleInput1 + " minBrakeThreshold " + minBrakeThreshold);
if (brakeEventStartTime > 0 && playerControlCar.Car.ThrottleInput1 > minBrakeThreshold)
{
brakeEventDifferenceTime = Time.time - brakeEventStartTime;
Debug.Log("Brake event diff " + brakeEventDifferenceTime);
Debug.Log("Throttle " + playerControlCar.Car.ThrottleInput1);
averageEventReactionTime.Add(brakeEventDifferenceTime);
brakeEventStartTime = 0;
safetyDistanceCheck.GetSafetyDistance(SafetyDistance);
}
}
private void SafetyDistance(float obj)
{
totalSafetyDistance += obj;
++totalSafetyDistanceCount;
}
/// <summary>
/// Records the time when the car ahead starts braking
/// </summary>
protected void BrakeCarEvent()
{
brakeEventStartTime = Time.time;
Debug.Log("BrakeCarEvent ");
}
/// <summary>
/// calculates the average time taken to react
/// </summary>
public float GetEventReactionTimeAvg()
{
float avg = 0;
foreach (var item in averageEventReactionTime)
{
avg += item;
}//noofevents
return avg / (float)averageEventReactionTime.Count;
}
}
}
EDIT
I create new class FearTest:
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace DrivingTest
{
public class FearTest : MonoBehaviour
{
//public GameObject redDot;
public TextMeshPro textAnsia2;
public TextMesh textAnsia3;
public int shouldEvaluateTest = 0; // 0 - false, 1 - true
public int secondsInterval;
public int secondsToDisaply;
public int radiusR1;
public int radiusR2;
public int reactionTestDuration;
public int maxDots;
public float dotRadius = 1;
private float endTime;
private float dotDisaplyAtTime;
private List<float> dotDisplayReactTime = new List<float>();
private int missedSignals;
private int currentDotsCount;
private bool isReactionAlreadyGiven;
public bool ShouldEvaluateTest
{
get
{
return shouldEvaluateTest != 0;
}
}
void OnEnable()
{
GameManager.TestSceneLoaded += OnSceneLoaded;
}
private void OnDisable()
{
GameManager.TestSceneLoaded -= OnSceneLoaded;
}
#region Scene Loaded
private void OnSceneLoaded(eTESTS test)
{
if (SceneManager.GetActiveScene().name != test.ToString())
return;
Reset();
GetApiParams();
}
#endregion
private void GetApiParams()
{
#if UNITY_EDITOR
if (!GameManager.Instance)
{
Init();
return;
}
#endif
Debug.Log("called rt");
NextTestOutput nextTestOutput = GameManager.Instance.currentTest;
if (nextTestOutput.content != null && nextTestOutput.content.Count > 0)
{
foreach (var item in nextTestOutput.content[0].listInputParameter)
{
switch ((ePARAMETERS)item.id)
{
case ePARAMETERS.RT_ENABLE:
shouldEvaluateTest = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RT_DURATION:
reactionTestDuration = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RED_DOT_FREQ:
secondsInterval = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RED_DOT_MAX_COUNT:
maxDots = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RED_DOT_RADIUS_R1:
radiusR1 = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RED_DOT_RADIUS_R2:
radiusR2 = System.Convert.ToInt32(item.Value);
break;
case ePARAMETERS.RED_DOT_SIZE:
dotRadius = float.Parse(item.Value)/10f;
break;
case ePARAMETERS.RED_DOT_TIME:
secondsToDisaply = System.Convert.ToInt32(item.Value);
break;
}
}
Debug.Log("called rt "+ shouldEvaluateTest);
/*if (ShouldEvaluateTest)
{
Init();
}*/
Init();//dopo bisogna sistemare il shoudl evaluatetest
}
}
Coroutine displayCoroutine;
public void Init()
{
endTime = Time.time + reactionTestDuration + 10;
SetRedDotSize();
displayCoroutine = StartCoroutine(RedDotDisplay());
}
private IEnumerator RedDotDisplay()
{
yield return new WaitForSeconds(2);
while (true)
{
SetRandomDotPosition();
RedDot(true);
isReactionAlreadyGiven = false;
dotDisaplyAtTime = Time.time;
currentDotsCount++;
yield return new WaitForSeconds(secondsToDisaply);
if(!isReactionAlreadyGiven)
{
missedSignals++;
}
RedDot(false);
if ((reactionTestDuration > 0 && endTime <= Time.time) || (maxDots > 0 && currentDotsCount >= maxDots))
break;
float waitTime = secondsInterval - secondsToDisaply;
yield return new WaitForSeconds(waitTime);
}
}
private void Update()
{
if (!ShouldEvaluateTest)
return;
if (!isReactionAlreadyGiven && /*redDot.activeSelf &&*/ (LogitechGSDK.LogiIsConnected(0) && LogitechGSDK.LogiButtonIsPressed(0, 23)))
{
isReactionAlreadyGiven = true;
float reactionTime = Time.time - dotDisaplyAtTime;
Debug.Log("Reaction Time RT : " + reactionTime);
RedDot(false);
dotDisplayReactTime.Add(reactionTime);
}
}
public double GetReactionTimeAvg()
{
double avg = 0;
foreach (var item in dotDisplayReactTime)
{
avg += item;
}
//avg / currentDotsCount
return avg / (float)dotDisplayReactTime.Count;
}
public double GetMissedSignals()
{
return ((float) missedSignals / (float) currentDotsCount) * 100;
}
private void RedDot(bool state)
{
//redDot.SetActive(state);
textAnsia2.SetText("Pippo 2");
}
private void SetRedDotSize()
{
//redDot.transform.localScale *= dotRadius;
textAnsia2.transform.localScale *= dotRadius;
textAnsia3.transform.localScale *= dotRadius;
}
private void SetRandomDotPosition()
{
//redDot.GetComponent<RectTransform>().anchoredPosition = GetRandomPointBetweenTwoCircles(radiusR1, radiusR2)*scale;
float scale = ((Screen.height*0.9f) / radiusR2) * 0.9f;
Vector3 pos = GetRandomPointBetweenTwoCircles(radiusR1, radiusR2) * scale;
Debug.Log("RT temp pos : " + pos);
pos = new Vector3(500, 500, 0);
Debug.Log("RT pos : " + pos);
// redDot.transform.position = pos;
Vector3 pos2 = new Vector3(20, 20, 0);
Debug.Log("text ansia 2 : " + pos2);
textAnsia2.transform.position = pos2;
textAnsia3.transform.position = pos;
}
#region Getting Red Dot b/w 2 cricles
/*
Code from : https://gist.github.com/Ashwinning/89fa09b3aa3de4fd72c946a874b77658
*/
/// <summary>
/// Returns a random point in the space between two concentric circles.
/// </summary>
/// <param name="minRadius"></param>
/// <param name="maxRadius"></param>
/// <returns></returns>
Vector3 GetRandomPointBetweenTwoCircles(float minRadius, float maxRadius)
{
//Get a point on a unit circle (radius = 1) by normalizing a random point inside unit circle.
Vector3 randomUnitPoint = Random.insideUnitCircle.normalized;
//Now get a random point between the corresponding points on both the circles
return GetRandomVector3Between(randomUnitPoint * minRadius, randomUnitPoint * maxRadius);
}
/// <summary>
/// Returns a random vector3 between min and max. (Inclusive)
/// </summary>
/// <returns>The <see cref="UnityEngine.Vector3"/>.</returns>
/// <param name="min">Minimum.</param>
/// <param name="max">Max.</param>
Vector3 GetRandomVector3Between(Vector3 min, Vector3 max)
{
return min + Random.Range(0, 1) * (max - min);
}
#endregion
#region Reset
private void Reset()
{
if (displayCoroutine != null)
StopCoroutine(displayCoroutine);
RedDot(false);
shouldEvaluateTest = 0;
reactionTestDuration = 0;
secondsInterval = 0;
missedSignals = 0;
maxDots = 0;
radiusR1 = 0;
radiusR2 = 0;
dotRadius = 1;
secondsToDisaply = 0;
endTime = 0;
dotDisaplyAtTime = 0;
dotDisplayReactTime.Clear();
//redDot.transform.localScale = Vector3.one;
textAnsia2.transform.localScale = Vector3.one;
textAnsia3.transform.localScale = Vector3.one;
currentDotsCount = 0;
isReactionAlreadyGiven = true;
}
#endregion
}
}
When "SetRandomDotPosition" method is called, in my scene I can display in a random position of the screen the RedDot (that is a simple image with a red dot), but I m not able to display my textAnsia2. How can I fixed it ?
I hope this will help you to get started :
https://vimeo.com/709527359
the scripts:
https://imgur.com/a/csNKwEh
I hope that in the video i covered every step that i've made.
Edit: The only thing is for you to do is to make the input of the Wheel to work on the slider. Try to make a simple script: when the text is enabled then the input from a joystick to work on that . When is disabled switch it back for his purpose .

Sewing up two meshes

Good afternoon! I'm trying to sew up two meshes. I do this as follows: first I convert the sprite into a mesh, then I duplicate the resulting mesh, shift it along the "z" axis, invert it, and then sew it up. But I faced such a problem: he sews rectangular meshes well, but in circular meshes there are some defects on the sides. So, how can you sew up these sides? (Materials and code)
public class ConvertSpriteInMesh : MonoBehaviour
{
public Sprite sprite;
private MeshDraft meshDraft = new MeshDraft();
private Mesh mesh;
void Start()
{
GetComponent<MeshFilter>().mesh = SpriteToMesh(sprite);
SewingUp();
}
/// <summary>
/// Sewing up nets
/// </summary>
private void SewingUp()
{
mesh = GetComponent<MeshFilter>().mesh;
meshDraft = new MeshDraft(mesh);
int leftVertical = mesh.vertices.Length / 2; // getting the beginning of the left vertical of the mesh
int index = mesh.vertices.Length;
for (int i = 0; i < leftVertical - 1; i++)
{
meshDraft.AddQuad(mesh.vertices[i], mesh.vertices[i+1], mesh.vertices[i + leftVertical + 1],mesh.vertices[i+leftVertical],
index);
index += 4;
}
GetComponent<MeshFilter>().mesh = meshDraft.ToMesh(); // assign the resulting mesh
}
/// <summary>
/// Convert Sprite to Mesh
/// </summary>
/// <param name="_sprite"></param>
/// <returns></returns>
private Mesh SpriteToMesh(Sprite _sprite)
{
// declaring variables
Mesh mesh = new Mesh();
Vector3[] _verticles;
int[] _triangle;
// assigning values
_verticles = Array.ConvertAll(_sprite.vertices, i => (Vector3)i);
_triangle = Array.ConvertAll(_sprite.triangles, i => (int)i);
// changing the size of the array
Array.Resize(ref _verticles, _verticles.Length * 2);
Array.Resize(ref _triangle, _triangle.Length * 2);
// adding another side
for (int i = 0; i < _verticles.Length / 2; i++)
{
_verticles[_verticles.Length / 2 + i] = new Vector3(_verticles[i].x, _verticles[i].y, 0.5f);
}
for (int i = 0; i < _triangle.Length / 2; i++)
{
_triangle[_triangle.Length / 2 + i] = _triangle[i] + (_verticles.Length / 2);
}
// invert the second side
for(int i = _triangle.Length / 2; i < _triangle.Length; i += 3) {
var temp = _triangle[i];
_triangle[i] = _triangle[i + 1];
_triangle[i + 1] = temp;
}
// assigning the mesh
mesh.vertices = _verticles;
mesh.triangles = _triangle;
mesh.RecalculateBounds();
mesh.RecalculateNormals();
return mesh;
}
}
public partial class MeshDraft {
public string name = "";
public List<Vector3> vertices = new List<Vector3>();
public List<int> triangles = new List<int>();
public List<Vector3> normals = new List<Vector3>();
public List<Vector4> tangents = new List<Vector4>();
public List<Vector2> uv = new List<Vector2>();
public List<Vector2> uv2 = new List<Vector2>();
public List<Vector2> uv3 = new List<Vector2>();
public List<Vector2> uv4 = new List<Vector2>();
public List<Color> colors = new List<Color>();
public MeshDraft(Mesh mesh) {
name = mesh.name;
vertices.AddRange(mesh.vertices);
triangles.AddRange(mesh.triangles);
normals.AddRange(mesh.normals);
tangents.AddRange(mesh.tangents);
uv.AddRange(mesh.uv);
uv2.AddRange(mesh.uv2);
uv3.AddRange(mesh.uv3);
uv4.AddRange(mesh.uv4);
colors.AddRange(mesh.colors);
}
public void AddQuad(Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3, int index, Color color = default(Color)) {
vertices.Add(v0);
vertices.Add(v1);
vertices.Add(v2);
vertices.Add(v3);
Vector3 normal0 = Vector3.Cross(v2 - v1, v3 - v1).normalized;
Vector3 normal1 = Vector3.Cross(v1 - v0, v2 - v0).normalized;
normals.Add(normal0);
normals.Add(normal0);
normals.Add(normal1);
normals.Add(normal1);
colors.Add(color);
colors.Add(color);
colors.Add(color);
colors.Add(color);
triangles.Add(index);
triangles.Add(index + 1);
triangles.Add(index + 2);
triangles.Add(index);
triangles.Add(index + 2);
triangles.Add(index + 3);
}
public Mesh ToMesh() {
var mesh = new Mesh { name = name };
mesh.SetVertices(vertices);
mesh.SetTriangles(triangles, 0);
mesh.SetNormals(normals);
mesh.SetTangents(tangents);
mesh.SetUVs(0, uv);
mesh.SetUVs(1, uv2);
mesh.SetUVs(2, uv3);
mesh.SetUVs(3, uv4);
mesh.SetColors(colors);
return mesh;
}
Successful stitching (screen)
Bad stitching (screen)
I was given an answer on another forum, who is interested, I will leave a link here - https://www.cyberforum.ru/unity/thread2823987.html

Dynamically adding tiles to a grid based map

I want to have an infinitely explorable map. The plan is to create categories of game tiles (roads, obstacles, buildings), and randomly choose a category of game tile to be added when the player approaches the edge of the existing set of tiles. Tiles will also be destroyed once the player is 2 grid squares away from that tile. Currently I am using a multidimensional array that requires a size initializer.
What I have so far:
public class GameManager : MonoBehaviour
{
private GameObject[,] tileArray;
public GameObject groundTile;
public GameObject player;
private int tileSize = 80;
private int nextFarX = 1;
private int nextFarZ = 1;
private int nextNearX = -1;
private int nextNearZ = -1;
private float padding = .1f;
private int arrayOffset;
private int arrayDimension;
// Use this for initialization
void Start ()
{
arrayDimension = 200;
arrayOffset = arrayDimension / 2;
tileArray = new GameObject[,];
this.AddCubeAt(0, 0);
}
// Update is called once per frame
void Update () {
var x = Convert.ToInt32(player.transform.position.x / tileSize);
var z = Convert.ToInt32(player.transform.position.z / tileSize);
for (int i = -1; i < 2; i++)
{
for (int j = -1; j < 2; j++)
{
var checkX = x + i;
var checkZ = z + j;
if (tileArray[checkX + arrayOffset, checkZ + arrayOffset] == null)
{
//player is less than 2 tiles away from this grid, add a tile
this.AddCubeAt(checkX, checkZ);
}
}
}
// feels like a hack, but it will remove tiles that are not touching the tile that the player occupies
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 6; j++)
{
if (i == 0 | i == 5 | j == 0 | j == 5)
{
if (tileArray[x + (i-2) + arrayOffset, z + (j-2) + arrayOffset] != null)
{
Destroy(tileArray[x + (i - 2) + arrayOffset, z + (j - 2) + arrayOffset]);
tileArray[x + (i - 2) + arrayOffset, z + (j - 2) + arrayOffset] = null;
}
}
}
}
}
private void AddCubeAt(int x, int z)
{
var pos = new Vector3(x * tileSize, 0, z * tileSize);
var rot = Quaternion.identity;
GameObject newCube = (GameObject)Instantiate(groundTile, pos, rot);
tileArray[x + arrayOffset, z + arrayOffset] = newCube;
}
}
What is a better way to approach this?
You should familiarize yourself with Graph Data Structure (not adjacency matrix implementation). It's much more appropriate for this task. And, I would solve this
Tiles will also be destroyed once the player is 2 grid squares away from that tile
in another way: Every time player changed his position I would start DFS on target depth (in your case it's 2) and remove found tiles.
Decided to go with a simple Dictionary and methods to query/update it:
private GameObject RetrieveTileAt(int x, int z)
{
string key = string.Format("{0}.{1}", x, z);
if (tileDictionary.ContainsKey(key))
{
return tileDictionary[key];
}
else
{
return null;
}
}
private void InsertTileAt(int x, int z, GameObject tile)
{
string key = string.Format("{0}.{1}", x, z);
tileDictionary[key] = tile;
}
It is not an infinitely sized grid, (int min + int max)squared, but it should be far more than I need.

Unity Roguelike Project: Argument Out of Range Exception

I'm getting an Argument Out of Range Exception for this script I'm following in a Unity tutorial.
Here's the exception:
ArgumentOutOfRangeException: Argument is out of range.
Parameter name: index
System.Collections.Generic.List`1[UnityEngine.Vector3].get_Item (Int32 index) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:225)
BoardManager.RandomPosition () (at Assets/Scripts/BoardManager.cs:75)
And here's the actual script for the project:
using UnityEngine;
using System; //Allows serializable attribute, which modifies how variables appear
using System.Collections.Generic; //Generic allows use of lists
using Random = UnityEngine.Random; //Used because Random class is in both System and Unity engine namespaces
public class BoardManager : MonoBehaviour
{
[Serializable]
public class Count
{
public int maximum;
public int minimum;
public Count (int min, int max) //Assignment constructor for Count to set values of min, max when we declare a new Count
{
minimum = min;
maximum = max;
}
}
public int columns = 8; //8x8 gameboard
public int rows = 8;
public Count wallCount = new Count(5, 9); //Specifies random range for walls to spawn in each level
public Count foodCount = new Count(1, 5); //Random range for food spawns
public GameObject exit; //Variable that holds exit prefab
public GameObject[] floorTiles; //Game Object Arrays hold our different prefabs to choose between
public GameObject[] wallTiles;
public GameObject[] foodTiles;
public GameObject[] enemyTiles;
public GameObject[] outerWallTiles;
private Transform boardHolder; //Manages Hierarchy
private List<Vector3> gridPositions = new List<Vector3>(); //Declares a private list of Vector3s
//Tracks all possible positions on gameboard, and keeps track of whether object has been spawned in that position or not
void InitialiseList()
{
gridPositions.Clear();
for (int x = 1; x < columns - 1; x++) //Nested FOR loops to fill list with each of the positions on our gameboard as a Vector3
{
for (int y = 1; y < rows - 1; y++)
{
gridPositions.Add(new Vector3(x, y, 0f)); //Adds x and y positions to the list
}
}
}
void BoardSetup() //Sets up outer wall and floor of gameboard
{
boardHolder = new GameObject("Board").transform;
for (int x = -1; x < columns + 1; x++)
{
for (int y = -1; y < rows + 1; y++)
{
GameObject toInstantiate = floorTiles[Random.Range(0, floorTiles.Length)]; //Defines a new GameObject
if (x == -1 || x == columns || y == -1 || y == rows)
{
toInstantiate = outerWallTiles[Random.Range(0, outerWallTiles.Length)];
}
GameObject instance = Instantiate(toInstantiate, new Vector3(x, y, 0f), Quaternion.identity) as GameObject;
//Instantiates a new GameObject. Rotation is always the same. Used as GameObject.
instance.transform.SetParent(boardHolder);
}
}
}
Vector3 RandomPosition()
{
try
{
int randomIndex = Random.Range(0, gridPositions.Count);
Vector3 randomPosition = gridPositions[randomIndex]; //THROWING AN EXCEPTION
gridPositions.RemoveAt(randomIndex); //Prevents duplicate position spawns
return randomPosition;
}
catch (Exception ex1)
{
Debug.Log(ex1);
throw;
}
}
void LayoutObjectAtRandom(GameObject[] tileArray, int minimum, int maximum)
{
int objectCount = Random.Range(minimum, maximum + 1);
for (int i = 0; i < objectCount; objectCount++)
{
Vector3 randomPosition = RandomPosition();
GameObject tileChoice = tileArray[Random.Range(0, tileArray.Length)];
Instantiate(tileChoice, randomPosition, Quaternion.identity);
}
}
public void SetupScene(int level) //Called by the GameManager
{
BoardSetup();
InitialiseList();
LayoutObjectAtRandom(wallTiles, wallCount.minimum, wallCount.maximum);
LayoutObjectAtRandom(foodTiles, foodCount.minimum, foodCount.maximum);
int enemyCount = (int)Mathf.Log(level, 2f); //Scales enemy count based on level
LayoutObjectAtRandom(enemyTiles, enemyCount, enemyCount);
Instantiate(exit, new Vector3(columns - 1, rows - 1, 0f), Quaternion.identity);
}
}
I'm assuming that the RandomPositions() function is throwing the exception, though I am unable to find a problem with the code. Does anyone see the problem?