I have this script:
public Sprite[] Images; //Index starts at one because we are setting the first sprite in Start() method
public int _Index = 1;
public float width;
public float height;
void Start(){
//Set the image to the first one
this.gameObject.GetComponent<SpriteRenderer>().sprite = Images[0];
}
public void onClick(){
//Reset back to 0 so it can loop again if the last sprite has been shown
if (_Index >= Images.Length)
_Index = 0;
//Set the image to array at element index, then increment
this.gameObject.GetComponent<SpriteRenderer>().sprite = Images[_Index++];
}
that works perfectly, what it does is that it changes the image of and object OnClick once all images has been shown it loops. However I can't scale my images manually in my scene window nor through script. Is there an easier way of changing my script to be able to manually scaling my images in my scene window. (I would rather scale my images in my scene window rather than through script because I would find it easier) Please and thank you :)
Second Edit
This is what my script looks like now:
public Sprite[] Images;
//Index starts at one because we are setting the first sprite in Start() method
public int _Index = 1;
public float width;
public float height;
public float lastWidth;
public float lastHeight;
void ResizeMe()
{
this.width = 1.567892f;
this.height = 1.07f;
}
void Update()
{
if(this.width != lastWidth || this.height != lastHeight)
{
ResizeMe (this.width, this.height);
this.lastWidth = width;
this.lastHeight = height;
}
}
void Start(){
//Set the image to the first one
this.gameObject.GetComponent<SpriteRenderer>().sprite = Images[0];
}
public void onClick(){
//Reset back to 0 so it can loop again if the last sprite has been shown
if (_Index >= Images.Length)
_Index = 0;
//Set the image to array at element index, then increment
this.gameObject.GetComponent<SpriteRenderer>().sprite = Images[_Index++];
}
Thank you :)
declare 2 more variables
lastWidth
lastHeight
in the Update() function do something like
if(this.width != lastWidth || this.height != lastHeight)
{
setSize(this.width,this.height)
this.lastWidth = width;
this.lastHeight = height;
}
create a setSize function to do the actual resizing.
Now when you change the public width,height variables from the editor the update will pick on the change and run your resize script
Related
guys so I'm trying to make an endless Map Generator for my 2D grappling Hook Game Kinda like Danis square Game and I was trying to generate a set number of swingable points in certain range to test.
Everything works fine except that some Points overlap each other when spawned So I was wondering how could I prevent this from happening?
Should I change my code or is there any method of spawning things I should try so they don't overlap?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MapGenerator : MonoBehaviour
{
public Transform Endpositin;
public int NumOfSwwingablePoints;
public GameObject SwinggablePoint;
public int MinX, MaxX;
public int MinY, MaxY;
public int Xpos;
public int Ypos;
Vector2 PointPos;
void Start()
{
for (int i = 0; i <= NumOfSwwingablePoints; i++)
{
Xpos = Random.Range(MinX, MaxX);
Ypos = Random.Range(MinY, MaxY);
PointPos = new Vector2(Xpos, Ypos);
Instantiate(SwinggablePoint, PointPos, Quaternion.identity);
}
}
// Update is called once per frame
void Update()
{
}
}
I have Tried this Method and I have also tried to increase values and giving the points colliders to see if they overlap but none of that works. I have also tried update function, but they just keep spawning, and it never stops
My suggestion is everytime you create a new block then save the position of the block in a list.
when next time you create a new block, check the new position with the list.
Here is the code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MapGenerator : MonoBehaviour
{
public Transform Endpositin;
public int NumOfSwwingablePoints;
public GameObject SwinggablePoint;
public int MinX, MaxX;
public int MinY, MaxY;
private int Xpos;
private int Ypos; // this can be private since we use only privatly
Vector2 PointPos;
List<Vector2> PointPosList = new List<Vector2>();
void Start()
{
for (int i = 0; i < NumOfSwwingablePoints; i++) // I change <= to < since we start from 0
{
Generating();
}
}
void Generating()
{
bool foundSamePosition;
do
{
foundSamePosition = false;
Xpos = Random.Range(MinX, MaxX);
Ypos = Random.Range(MinY, MaxY);
PointPos = new Vector2(Xpos, Ypos);
// finding the generated position is already exist
for (int i = 0; i < PointPosList.Count; i++)
{
if (SamePosition(PointPos, PointPosList[i]))
{
foundSamePosition = true;
}
}
} while (foundSamePosition);// if we found the position try again
// if not found, add the new position to a list for next time check
PointPosList.Add(PointPos);
Instantiate(SwinggablePoint, PointPos, Quaternion.identity);
}
private bool SamePosition(Vector2 v1, Vector2 v2)
{
return (v1.x == v2.x && v1.y == v2.y);
}
}
I was trying to use a for loop to instantiate cube as much fit on the screen. So i checked how wide the screen is and is checked how wide my cube was. Divided it by each other and it didnt work.
So what i think i did wrong is that Screen.width is in pixels and renderer.bounds.size.x is in world size.
How do i fix this? I didnt figure it out on the internet..
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class move : MonoBehaviour
{
public GameObject cubePrefab;
public static bool spawn;
float times;
int screenWidth;
float objectSize;
// Use this for initialization
void Start()
{
spawn = true;
screenWidth = Screen.width;
objectSize = GameObject.Find("vork").GetComponent<Renderer>().bounds.size.x;
times = screenWidth / objectSize;
}
// Update is called once per frame
void Update()
{
if(spawn == true)
{
for(int i=0; i < times;i++) {
Vector3 pos = new Vector3(i * 2, i *2, 0);
Instantiate(cubePrefab, pos, transform.rotation);
}
spawn = false;
}
}
}
2D game
You need to calculate pixel width. In order to calculate screen position from world position use Camera.WorldToScreenPoint. This is how a function could look like:
public static float GetPixelWidth(GameObject gO)
{
Bounds bounds = gO.GetComponent<Renderer>().bounds;
Vector3 boundsPos1 = Camera.main.WorldToScreenPoint(new Vector3(gO.transform.position.x + bounds.extents.x, gO.transform.position.y, gO.transform.position.y));
Vector3 boundsPos2 = Camera.main.WorldToScreenPoint(new Vector3(gO.transform.position.x - bounds.extents.x, gO.transform.position.y, gO.transform.position.y));
return Vector3.Distance(boundsPos1, boundsPos2);
}
And this is how to use the function in your code:
// Use this for initialization
void Start()
{
spawn = true;
screenWidth = Camera.main.pixelWidth;
objectSize = GetPixelWidth(GameObject.Find("vork"));
times = screenWidth / objectSize;
}
I have tested and it works with orthographic camera
I'm trying to dynamically resize particles using a slider, as well as change their colour.
Particles are used to display datapoints in a 3D scatterplot. I'm using this code: https://github.com/PrinzEugn/Scatterplot_Standalone
private ParticleSystem.Particle[] particlePoints;
void Update () {
pointScale = sizeSlider.value;
for (int i = 0; i < pointList.Count; i++) {
Quaternion quaternion = Camera.current.transform.rotation;
Vector3 angles = quaternion.eulerAngles;
// Set point color
particlePoints[i].startColor = new Color(angles.x, angles.y, angles.z, 1.0f);
particlePoints[i].transform.localScale = new Vector3(pointScale, pointScale, pointScale);
}
}
The issue is that there's no transform method for Particles, and changing the "startColour" doesn't change anything.
The API states that "The current size of the particle is calculated procedurally based on this value and the active size modules."
What does that mean, and how can I change the size of the particles ?
Thanks to previous answers I managed to get this working:
In the PlacePrefabPoints method I add every instantiated prefab to a List, and I add a listener to the slider, which looks like this:
void changedPointSize(){
pointScale = sizeSlider.value;
for (int i = 0; i < objects.Count; i++) {
objects[i].transform.localScale = new Vector3(pointScale, pointScale, pointScale);
}
}
Thanks all !
I just had a look at PointRenderer.cs -> CreateParticles and PlacePrefabPoints give a good hint what has to be changed.
So I guess you would simply change the scale values
foreach (var point in particlePoints)
{
Quaternion quaternion = Camera.current.transform.rotation;
Vector3 angles = quaternion.eulerAngles;
// Set point color
point.startColor = new Color(angles.x, angles.y, angles.z, 1.0f);
point.startSize = sizeSlider.value;
}
and than re-call
GetComponent<ParticleSystem>().SetParticles(particlePoints, particlePoints.Length);
it is questionable though if you really would do this in Update. I would rather do it in sizeSlider.onValueChanged to only do it when neccesarry (you could even make a certain treshold that has to be changed before updating the view) but well for the color there might be no other option than doing it in Update but atleast there I would use a Threshold:
privtae ParticleSystem ps;
// I assume you have that referenced in the inspector
public Slider sizeSlider;
// flag to control whether system should be updated
private bool updateSystem;
privtae void Awake()
{
ps = GetComponent<ParticleSystem>();
}
private void OnEnable()
{
// add a listener to onValueChanged
// it is secure to remove it first (even if not there yet)
// this makes sure it is not added twice
sizeSlider.onValueChanged.RemoveListener(OnsliderChanged());
sizeSlider.onValueChanged.AddListener(OnsliderChanged());
}
private void OnDisable()
{
// cleanup listener
sizeSlider.onValueChanged.RemoveListener(OnsliderChanged());
}
private void OnSliderChanged()
{
foreach (var point in particlePoints)
{
point.startSize = sizeSlider.value;
}
// do the same also for the instantiated prefabs
foreach(Transform child in PointHolder.transform)
{
child.localScale = Vecto3.one * sizeSlider.value;
}
updateSystem = true;
}
private Quaternion lastCameraRot;
public float CameraUpdateThreshold;
private void Update()
{
if(Quaternion.Angle(Camera.current.transform.rotation, lastCameraRot) > CameraUpdateThreshold)
{
foreach (var point in particlePoints)
{
Quaternion quaternion = Camera.current.transform.rotation;
Vector3 angles = quaternion.eulerAngles;
// Set point color
point.startColor = new Color(angles.x, angles.y, angles.z, 1.0f);
}
lastCameraRot = Camera.current.transform.rotation;
updateSystem = true;
}
if(!updateSystem) return;
updateSystem = false;
ps.SetParticles(particlePoints, particlePoints.Length);
}
As the title pointed out, for some reason when my game is paused my co-routine still runs. I even went as far as to put the time scale condition in a while condition so that the while doesnt run if it paused but to no avail. I've added my code in it's entirety and hope that someone will be able to assist.
using UnityEngine;
using System.Collections;
using Chronos;
public class ObjectSpawn : BaseBehaviour //MonoBehaviour
{
public float minTime = 3f;
public float maxTime = 9f;
public float minX = -65.5f;
public float maxX = -5.5f;
public float topY = -5.5f;
public float z = 0.0f;
public int count = 50;
public GameObject prefab;
public bool doSpawn = true;
public float fallGrav =1.0f;
int first = 1;
void Start()
{
Clock clock = Timekeeper.instance.Clock("MovingOneWayPlatforms");
StartCoroutine(Spawner());
}
IEnumerator Spawner()
{
while (first == 1) {
yield return time.WaitForSeconds(8.0f);
first = 0;
}
while (doSpawn && count > 0 /*&& time.timeScale != 0 */)
{
Renderer renderer = GetComponent<Renderer>();
float min = renderer.bounds.min.x;
float max = renderer.bounds.max.x;
Vector3 v12 = new Vector3(Random.Range(minX, maxX), this.gameObject.transform.position.y, 0f);
prefab.GetComponent<Rigidbody2D>().gravityScale = fallGrav;
prefab = Instantiate(prefab, v12, Random.rotation);
count--;
// yield return new WaitForSeconds(Random.Range(minTime, maxTime));
yield return time.WaitForSeconds(Random.Range(minTime, maxTime));
Destroy(prefab, 6);
}
}
}
Try to uncomment your 2nd while statement, I think that is your problem.
I am new and still not used to Chronos.
Maybe I'm wrong but my guess is this line.
Destroy(prefab, 6);
In my understanding, Destroy's delay should not affected by chronos.
You better use new Coroutine to destroy it.
like this
StartCoroutine(DestroyRoutine(prefab))
IEnumurator DestroyRoutine(GameObject gameobject)
{
yield return time.WaitForSeconds(6);
Destroy(gameObject)
}
ScrollView performance is a real drag (get it?), especially on mobile platforms. I frequently found myself getting less that 15 fps which left the user experience feeling jolting and underwhelming. After a lot of research and testing I have compiled a checklist to drastically improve performance. I now get at least 30 fps, with the majority of the CPU time allocated to WaitForTargetFPS.
I hope this helps anyone who is also having trouble in this area. Optimization solutions are hard to come by. Feel free to use and modify any of my code here.
ONE:
.GetComponent<>() calls are inefficient, especially outside the editor. Avoid using these in any kind of Update() method.
TWO:
OnValueChanged() is called every frame that the ScrollView is being dragged. It is therefore in a sense equivalent to Update() so you should avoid using .GetComponent<>() calls in this method.
THREE:
Whenever any element on a Canvas is changed the entire Canvas must rebuild its batches. This operation can be very expensive. It is therefore recommended to split your UI elements across at least two Canvases, one for elements that are changed rarely or never and one elements that change often.
Whenever a ScrollView scrolls the entire Canvas it is on is dirtied. It is therefore recommended that you have each ScrollView on a separate Canvas.
Unity Canvas Rebuilds Explanation: https://unity3d.com/learn/tutorials/topics/best-practices/fill-rate-canvases-and-input?playlist=30089
FOUR:
EventSystem.Update() handles the input detection in a scene, using raycasts to filter through the hierarchy in order to find a component to accept this input. Therefore these calculations are only done when interacting with the scene, like when a ScrollView is being scrolled. Removing unnecessary RaycastTarget properties from graphics and text will improve this processing time. It mightn't make a big difference, but if you're not careful enough objects can make the input handling time really add up.
FIVE:
With any kind of mask component, even a RectMask2D, all objects in a ScrollView are batched and rendered. If you have a lot of elements in your ScrollView, it is recommended that you utilize some kind of pooling solution. There are many of these available on the app store.
Unity Pooling Explanation: https://unity3d.com/learn/tutorials/topics/best-practices/optimizing-ui-controls
If, however, your project is incompatible with this, needing persistent elements, I would recommend that you hide your off screen objects to reduce performance overhead. Transform.SetParent() and GameObject.SetActive() are both resource intensive methods, instead attach a CanvasGroup component to each element and adjust the alpha value to achieve the same effect.
Here is a static script to detect if an object is visible or not and to set the alpha accordingly:
using UnityEngine;
using UnityEngine.UI;
public class ScrollHider : MonoBehaviour {
static public float contentTop;
static public float contentBottom;
static public bool HideObject(GameObject givenObject, CanvasGroup canvasGroup, float givenPosition, float givenHeight) {
if ((Mathf.Abs(givenPosition) + givenHeight > contentTop && Mathf.Abs(givenPosition) + givenHeight < contentBottom) || (Mathf.Abs(givenPosition) > contentTop && Mathf.Abs(givenPosition) < contentBottom)) {
if (canvasGroup.alpha != 1) {
canvasGroup.alpha = 1;
}
return true;
} else {
if (canvasGroup.alpha != 0) {
canvasGroup.alpha = 0;
}
return false;
}
}
static public void Setup(Scroll givenScroll) {
contentTop = (1 - givenScroll.verticalNormalizedPosition) * (givenScroll.content.rect.height - givenScroll.viewport.rect.height);
contentBottom = contentTop + givenScroll.viewport.rect.height;
}
}
SIX:
Unity's built in ScrollRect component allows for broad, modular functionality. However, in terms of performance it can be noticeably slower than if you were to write your own. Here is a Scroll script that achieves the same ends, but only supports the vertical, clamped and inertia properties of Unity's ScrollRect.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
public class Scroll : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler, IScrollHandler {
private Camera mainCamera;
private RectTransform canvasRect;
public RectTransform viewport;
public RectTransform content;
private Rect viewportOld;
private Rect contentOld;
private List<Vector2> dragCoordinates = new List<Vector2>();
private List<float> offsets = new List<float>();
private int offsetsAveraged = 4;
private float offset;
private float velocity = 0;
private bool changesMade = false;
public float decelration = 0.135f;
public float scrollSensitivity;
public OnValueChanged onValueChanged;
[System.Serializable]
public class OnValueChanged : UnityEvent { }
[HideInInspector]
public float verticalNormalizedPosition
{
get
{
float sizeDelta = CaculateDeltaSize();
if (sizeDelta == 0) {
return 0;
} else {
return 1 - content.transform.localPosition.y / sizeDelta;
}
}
set
{
float o_verticalNormalizedPosition = verticalNormalizedPosition;
float m_verticalNormalizedPosition = Mathf.Max(0, Mathf.Min(1, value));
float maxY = CaculateDeltaSize();
content.transform.localPosition = new Vector3(content.transform.localPosition.x, Mathf.Max(0, (1 - m_verticalNormalizedPosition) * maxY), content.transform.localPosition.z);
float n_verticalNormalizedPosition = verticalNormalizedPosition;
if (o_verticalNormalizedPosition != n_verticalNormalizedPosition) {
onValueChanged.Invoke();
}
}
}
private float CaculateDeltaSize() {
return Mathf.Max(0, content.rect.height - viewport.rect.height); ;
}
private void Awake() {
mainCamera = GameObject.Find("Main Camera").GetComponent<Camera>();
canvasRect = transform.root.GetComponent<RectTransform>();
}
private Vector2 ConvertEventDataDrag(PointerEventData eventData) {
return new Vector2(eventData.position.x / mainCamera.pixelWidth * canvasRect.rect.width, eventData.position.y / mainCamera.pixelHeight * canvasRect.rect.height);
}
private Vector2 ConvertEventDataScroll(PointerEventData eventData) {
return new Vector2(eventData.scrollDelta.x / mainCamera.pixelWidth * canvasRect.rect.width, eventData.scrollDelta.y / mainCamera.pixelHeight * canvasRect.rect.height) * scrollSensitivity;
}
public void OnPointerDown(PointerEventData eventData) {
velocity = 0;
dragCoordinates.Clear();
offsets.Clear();
dragCoordinates.Add(ConvertEventDataDrag(eventData));
}
public void OnScroll(PointerEventData eventData) {
UpdateOffsetsScroll(ConvertEventDataScroll(eventData));
OffsetContent(offsets[offsets.Count - 1]);
}
public void OnDrag(PointerEventData eventData) {
dragCoordinates.Add(ConvertEventDataDrag(eventData));
UpdateOffsetsDrag();
OffsetContent(offsets[offsets.Count - 1]);
}
public void OnPointerUp(PointerEventData eventData) {
dragCoordinates.Add(ConvertEventDataDrag(eventData));
UpdateOffsetsDrag();
OffsetContent(offsets[offsets.Count - 1]);
float totalOffsets = 0;
foreach (float offset in offsets) {
totalOffsets += offset;
}
velocity = totalOffsets / offsetsAveraged;
dragCoordinates.Clear();
offsets.Clear();
}
private void OffsetContent(float givenOffset) {
float newY = Mathf.Max(0, Mathf.Min(CaculateDeltaSize(), content.transform.localPosition.y + givenOffset));
if (content.transform.localPosition.y != newY) {
content.transform.localPosition = new Vector3(content.transform.localPosition.x, newY, content.transform.localPosition.z);
}
onValueChanged.Invoke();
}
private void UpdateOffsetsDrag() {
offsets.Add(dragCoordinates[dragCoordinates.Count - 1].y - dragCoordinates[dragCoordinates.Count - 2].y);
if (offsets.Count > offsetsAveraged) {
offsets.RemoveAt(0);
}
}
private void UpdateOffsetsScroll(Vector2 givenScrollDelta) {
offsets.Add(givenScrollDelta.y);
if (offsets.Count > offsetsAveraged) {
offsets.RemoveAt(0);
}
}
private void LateUpdate() {
if (viewport.rect != viewportOld) {
changesMade = true;
viewportOld = new Rect(viewport.rect);
}
if (content.rect != contentOld) {
changesMade = true;
contentOld = new Rect(content.rect);
}
if (velocity != 0) {
changesMade = true;
velocity = (velocity / Mathf.Abs(velocity)) * Mathf.FloorToInt(Mathf.Abs(velocity) * (1 - decelration));
offset = velocity;
}
if (changesMade) {
OffsetContent(offset);
changesMade = false;
offset = 0;
}
}
}
A nice article explains that the default targetFrameRate might be responsible for the unsmooth scrolling behavior of the scrollView. This can be resolved via:
Application.targetFrameRate = 60; // or whatever you wish. 60 turned out enough for us
Of course this setting is only effective if you have resolved the performance issues (as it is nicely explained by Phedg1).