How to add button in scroll View using script in unity - unity3d

I am new to unity and am using unity 2020.3.26f1 . I am receiving an array of names from server. I want to make buttons dynamically using those names inside scroll view using c# script and assign on Click() that simply prints the name of button when its clicked. I just don't know how to create button dynamically. Following is the script I have attached to scroll View;
public string allNames;
void Start()
{
StartCoroutine(getNames());
}
IEnumerator getNames()
{
UnityWebRequest www = UnityWebRequest.Get("http://localhost:8080/names");
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
{
Debug.Log(www.error);
}
else
{
allNames = www.downloadHandler.text;
allNames = allNames.Replace("[", "");
allNames = allNames.Replace("]", "");
allNames = allNames.Replace("\"","");
string[] separatedNames = allNames.Split(',');
for (int i = 0; i < separatedNames.Length; i++)
{
Debug.Log(separatedNames[i]);
//make buttons here and attach them to scroll View
}
}
}

I found a video that applied #ShafqatJamilKhan suggestion. Posting my code here for reference.
//content reference of scroll view
[SerializeField] Transform contentPanel;
[SerializeField] GameObject buttonPrefab;
void Start()
{
StartCoroutine(getNames());
}
IEnumerator getNames()
{
UnityWebRequest www = UnityWebRequest.Get("http://localhost:8080/names");
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
{
Debug.Log(www.error);
}
else
{
string allNames = www.downloadHandler.text;
allNames = allNames.Replace("[", "");
allNames = allNames.Replace("]", "");
allNames = allNames.Replace("\"","");
// prefab button y position and yPosition should be the same.
float yPosition = 115;
string[] separatedNames = allNames.Split(',');
for (int i = 0; i < separatedNames.Length; i++)
{
GameObject button = (GameObject)Instantiate(buttonPrefab);
string name = separatedNames[i];
button.GetComponentInChildren<Text>().text = name;
button.GetComponent<Button>().onClick.AddListener(
// your function whatever you want to do
() => { customFunction(name); });
// applying y positions so the buttons dont overlap
button.transform.SetPositionAndRotation(new Vector3(0.0f, yPosition, 0.0f), new Quaternion(0.0f, 0.0f, 0.0f,0.0f));
button.transform.SetParent(contentPanel,false);
yPosition -= 35;
}
}
}

Make a nice button and save it as prefab.
Instantiate it or Pool it.
Add onClick events.
GameObject buttonPrefab;
void MyAwesomeCreator()
{
GameObject go = Instantiate(buttonPrefab);
var button = GetComponent<UnityEngine.UI.Button>();
button.onClick.AddListener(() => FooOnClick());
}
void FooOnClick()
{
Debug.Log("Ta-Da!");
}
Copied from Unity Forum

Related

Creating top down 2D whip mechanic

What Whip should look like
I'm trying to create a whip that can extend in any direction the mouse is facing after pressing a certain button. If there are "grabbable" objects in the way such as an enemy or box, it should latch onto those objects and pull them around to collide with other objects for a certain amount of time.
I know that I need the different sprite shots of the whip extending and latching for animation, but I have no idea how to implement this in code and how to get the whip to stop short if it detects a "grabbable" object.
Attach this script to your player, this should get the job done:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public Transform player = null;
public float speed = 30f;
public string grabbableTag = "grabbable";
private LineRenderer line = null;
private float timer = 0f;
private bool grabbing = false;
private bool reached = false;
private Vector2 from = Vector2.zero;
private Vector2 to = Vector2.zero;
private Vector2 target = Vector2.zero;
private Transform grabbable = null;
private void Start()
{
player = transform;
line = new GameObject("Line").AddComponent<LineRenderer>();
line.startColor = Color.red;
line.endColor = Color.red;
// Assign a material
line.gameObject.SetActive(false);
reached = false;
grabbing = false;
}
private void Update()
{
if(grabbing)
{
Grabbing();
}
else
{
if (Input.GetMouseButtonDown(0))
{
Grab();
}
}
}
private void Grab()
{
Vector3 mousePosition = Input.mousePosition;
mousePosition.z = Vector3.Distance(player.position, Camera.main.transform.position);
to = Camera.main.ScreenToWorldPoint(mousePosition);
from = player.position;
Vector2 direction = (to - from).normalized;
float distance = Vector2.Distance(from, to);
RaycastHit2D[] hits = Physics2D.RaycastAll(from, direction, distance);
grabbable = null;
for (int i = 0; i < hits.Length; i++)
{
if (hits[i].transform.tag == grabbableTag)
{
grabbable = hits[i].transform;
break;
}
}
if (grabbable != null)
{
distance = Vector2.Distance(player.position, grabbable.position);
to = from + direction * distance;
}
grabbing = true;
reached = false;
target = from;
timer = 0;
line.gameObject.SetActive(true);
line.positionCount = 2;
line.SetPosition(0, from);
line.SetPosition(1, from);
}
private void Grabbing()
{
if (reached)
{
target = Vector2.Lerp(target, from, speed * Time.deltaTime);
if (target == from)
{
GrabDone(grabbable);
grabbing = false;
line.gameObject.SetActive(false);
}
}
else
{
target = Vector2.Lerp(target, to, speed * Time.deltaTime);
if(target == to)
{
reached = true;
}
}
line.SetPosition(1, target);
if (reached && grabbable != null)
{
grabbable.position = target;
}
}
private void GrabDone(Transform grabbed)
{
if(grabbed != null)
{
// Do somthing ...
Destroy(grabbed.gameObject);
}
}
}

Spring Joint 2D (maybe) causing transform.position errors

I know the title might be misleading after you see what is the problem, but I really don't know how to name this issue.
The first picture shows the problem.
The white line shows the distance between the player and the gameobject called hook. The blue sprite sphere close to the hook is the SpringJoint2D.connectedBody.
They both (the white line and the blue sprite) are working with the same value: hook.transform.position.
Here is the code snippets which I believe are causing problems or at least reveal the most:
SpringJoint2D snippet:
if (Input.GetMouseButtonDown(0))
{
hook = FindClosestObject(radius, "Hook");
if(hook != null)
{
joint.enabled = true;
joint.connectedBody = hook;
joint.connectedAnchor = hook.transform.position;
Debug.Log("Click.");
Debug.Log(hook);
}
}
if (Input.GetMouseButtonUp(0))
{
joint.enabled = false;
joint.connectedBody = null;
}
Debug.DrawLine snippet:
if (hook != null)
{
joint.distance = Vector3.Distance(hook.transform.position, transform.position) / 2;
Debug.DrawLine(transform.position, hook.transform.position);
}
And here is the whole code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerScript : MonoBehaviour
{
public float radius;
private SpringJoint2D joint;
private Rigidbody2D hook = new Rigidbody2D();
// Start is called before the first frame update
void Start()
{
joint = GetComponent<SpringJoint2D>();
}
// Update is called once per frame
void Update()
{
//touch.phase == TouchPhase.Began
if (Input.GetMouseButtonDown(0))
{
hook = FindClosestObject(radius, "Hook");
if(hook != null)
{
joint.enabled = true;
joint.connectedBody = hook;
joint.connectedAnchor = hook.transform.position;
Debug.Log("Click.");
Debug.Log(hook);
}
}
if (Input.GetMouseButtonUp(0))
{
joint.enabled = false;
joint.connectedBody = null;
}
//foreach (Touch touch in Input.touches)
//{
//}
if (hook != null)
{
joint.distance = Vector3.Distance(hook.transform.position, transform.position) / 2;
Debug.DrawLine(transform.position, hook.transform.position);
}
}
private void OnDrawGizmos()
{
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(transform.position, radius);
if (hook != null)
Gizmos.DrawLine(transform.position, hook.transform.position);
}
public Rigidbody2D FindClosestObject(float radius, string tag)
{
GameObject[] gos;
gos = GameObject.FindGameObjectsWithTag(tag);
Rigidbody2D closest = null;
float distance = radius;
Vector3 position = transform.position;
foreach (GameObject go in gos)
{
Vector3 diff = go.transform.position - position;
float curDistance = diff.sqrMagnitude;
if (curDistance < distance)
{
closest = go.GetComponent<Rigidbody2D>();
distance = curDistance;
}
}
return closest;
}
}
Ah, I was just stupid.
There is no need to assign the connectedAnchor if you also asign the connectedRigidbody2D
The connectedAnchor is the offset from the connectedRigidbody2D's position...

How do I play an animation, wait, then fade out? (In unity)

Basically what the title asks. I want to play my animation, then after the animation is finished playing, I want there to be a small delay before it fades out of the scene in about 2 seconds. After it fades out, it should be disabled and reset.
Here's my code so far, but when I set unlocking to true, it does nothing.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AnimationController : MonoBehaviour
{
public Animator anim;
public SpriteRenderer sr;
public bool unlocking;
Sprite unlockSprite;
Sprite lockSprite;
Color temp;
// Start is called before the first frame update
void Start()
{
unlocking = false;
unlockSprite = Resources.Load<Sprite>("unlock");
lockSprite = Resources.Load<Sprite>("lock");
}
// Update is called once per frame
void Update()
{
if (unlocking) {
anim.Play("unlock");
unlocking = false;
sr.sprite = unlockSprite;
System.Threading.Thread.Sleep(1000);
StartCoroutine(FadeTo(0.0f, 2.0f));
temp = new Color(sr.color.r, sr.color.g, sr.color.b, 0f);
sr.color = temp;
gameObject.SetActive(false);
sr.sprite = lockSprite;
temp = new Color(sr.color.r, sr.color.g, sr.color.b, 1f);
sr.color = temp;
}
}
IEnumerator FadeTo(float aValue, float aTime)
{
float alpha = sr.color.a;
for (float t = 0.0f; t < 1.0f; t += Time.deltaTime / aTime)
{
Color newColor = new Color(sr.color.r, sr.color.g, sr.color.b, Mathf.Lerp(alpha, aValue, t));
sr.color = newColor;
yield return null;
}
}
}
UPDATED CODE:
anim.Play("unlock");
unlocking = false;
if (animInfo.normalizedTime >= 1)
{
anim.enabled = false;
sr.sprite = unlockSprite;
StartCoroutine(FadeTo(0.0f, 2.0f));
temp = new Color(sr.color.r, sr.color.g, sr.color.b, 0f);
sr.color = temp;
gameObject.SetActive(false);
sr.sprite = lockSprite;
temp = new Color(sr.color.r, sr.color.g, sr.color.b, 1f);
sr.color = temp;
}
One of ways to pull it off, is to make another coroutine, something like that:
IEnumerator UnlockSequence()
{
while (animInfo.normalizedTime < 1.0f)
{
yield return null;
}
anim.enabled = false;
sr.sprite = unlockSprite;
yield return new WaitForSeconds(1.0f);
yield return StartCoroutine(FadeTo(0.0f, 2.0f));
// everything else
}

How to have same size of objects in different screen resolutions?

I'm working on a 2D game in Unity. I'm developing it for android and it's in portrait mode. I have placed one wall on left side of screen and one wall on right side of screen. The problem is the distance between those 2 walls is different in different screen resolutions. So, my character can't jump from one wall to other with the same velocity being applied.
I'm attaching an image to make my point clear.
What can I do so that in different devices, I get the same distance between these 2 walls? Any help will be appreciated.
EDIT: This is how I'm placing the walls right now.
screenLeft = Camera.main.ScreenToWorldPoint (new Vector3 (0f, 0f, 0f)).x;
screenRight = Camera.main.ScreenToWorldPoint (new Vector3 (Screen.width, 0f, 0f)).x;
rightWallSizeX = rightWall.GetComponent<SpriteRenderer> ().bounds.size.x;
leftWallSizeX = leftWall.GetComponent<SpriteRenderer> ().bounds.size.x;
rightWall.transform.position = new Vector3 (screenRight - rightWallSizeX/2, 0f, 0f);
leftWall.transform.position = new Vector3 (screenLeft + leftWallSizeX/2, 0f, 0f);
If you design your level using the editor:
Then the output aspect will be correct regardless its value:
As a bonus I've crafted you some code that lets the player stick to a wall and jump to the opposite side of it :D
using UnityEngine;
namespace Assets
{
public class PlayerController : MonoBehaviour
{
private const string Ground = "Ground";
private const string Wall = "Wall";
private Rigidbody2D _body;
private Vector2 _cNormal;
private GameObject _cWall;
private bool _joyAction;
private float _joyClimb;
private float _joyMove;
private State _state;
public float ForceClimb = 20.0f;
public float ForceJump = 5.0f;
public float ForceJumpFromWall = 10.0f;
public float ForceMove = 10.0f;
public float MaxMove = 15.0f;
private void Start()
{
_state = State.Air;
}
private void OnEnable()
{
_body = GetComponent<Rigidbody2D>();
}
private void Update()
{
_joyMove = Input.GetAxis("Horizontal");
_joyClimb = Input.GetAxis("Vertical");
_joyAction = Input.GetButtonDown("Fire1");
}
private void FixedUpdate()
{
var air = _state == State.Air;
var ground = _state == State.Ground;
var wall = _state == State.Wall;
if (air || ground)
{
var canJump = ground && _joyAction;
if (canJump)
{
var force = new Vector2(0.0f, ForceJump);
_body.AddForce(force, ForceMode2D.Impulse);
_state = State.Air;
}
var move = transform.InverseTransformDirection(_body.velocity).x;
if (move < MaxMove)
{
var force = new Vector2(_joyMove*ForceMove, 0.0f);
_body.AddRelativeForce(force);
}
}
else if (wall)
{
var climbing = Mathf.Abs(_joyClimb) > 0.0f;
if (climbing)
{
_body.AddForce(new Vector2(0, ForceClimb*_joyClimb));
}
else
{
var jumpingOut = _joyAction;
if (jumpingOut)
{
TryUnstickFromWall();
_body.AddForce(_cNormal*ForceJumpFromWall, ForceMode2D.Impulse);
}
}
}
}
private void OnGUI()
{
GUILayout.Label(_state.ToString());
}
public void OnCollisionEnter2D(Collision2D collision)
{
var c = collision.collider;
var t = c.tag;
if (t == Ground)
{
_state = State.Ground;
TryUnstickFromWall(); // fixes wall-sticking
}
else if (t == Wall && _state == State.Air) // jumping to wall
{
var wall = collision.gameObject;
var joint2D = wall.AddComponent<FixedJoint2D>();
var contact = collision.contacts[0];
var normal = contact.normal;
Debug.DrawRay(contact.point, normal, Color.white);
// stick 2 wall
joint2D.anchor = contact.point;
joint2D.frequency = 0.0f;
joint2D.autoConfigureConnectedAnchor = false;
joint2D.enableCollision = true;
_body.constraints = RigidbodyConstraints2D.FreezePositionX;
_body.gravityScale = 0.125f;
// save these
_cWall = wall;
_cNormal = normal;
// update state
_state = State.Wall;
}
}
public void OnCollisionExit2D(Collision2D collision)
{
if (collision.collider.tag == Ground)
{
_state = State.Air;
}
}
private void TryUnstickFromWall()
{
if (_cWall != null)
{
_body.constraints = RigidbodyConstraints2D.None;
_body.gravityScale = 1.0f;
var joint2D = _cWall.GetComponent<FixedJoint2D>();
if (joint2D != null) Destroy(joint2D);
_cWall = null;
}
}
}
internal enum State
{
Air,
Ground,
Wall
}
}
To try it:
create a layout like the one in picture 1
add a Rigidbody2D and BoxCollider2D to all of them
set their rigidbodies to be kinematic except for the player
add this script to the player
set walls tag to Wall
set ground tag to Ground
Then tweak it to your needs ...
EDIT:
You will have to encompass transform.InverseTransformDirection(_body.velocity).x with Mathf.Abs() to be correct, also at times the collision normal will be reversed making the player stuck to the wall, I let you figure this one :)

Make objects between camera and character transparent

I'm working on a script for my camera to make objects between itself and the character transparent.
I managed to make it work with RayCast however I don't know how to restablish objects alpha value after they escape the ray.
This is my current code:
private void XRay() {
float characterDistance = Vector3.Distance(transform.position, GameObject.Find("Character").transform.position);
Vector3 fwd = transform.TransformDirection(Vector3.forward);
RaycastHit hit;
if (Physics.Raycast(transform.position, fwd, out hit, characterDistance)) {
// Add transparence
Color color = hit.transform.gameObject.renderer.material.color;
color.a = 0.5f;
hit.transform.gameObject.renderer.material.SetColor("_Color", color);
}
}
This is my final code. Note it only makes transparent one object at a time, but the same implementation can easily be done with RaycastAll and using an array for oldHits.
public class Camara : MonoBehaviour {
RaycastHit oldHit;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void FixedUpdate() {
XRay ();
}
// Hacer a los objetos que interfieran con la vision transparentes
private void XRay() {
float characterDistance = Vector3.Distance(transform.position, GameObject.Find("Character").transform.position);
Vector3 fwd = transform.TransformDirection(Vector3.forward);
RaycastHit hit;
if (Physics.Raycast(transform.position, fwd, out hit, characterDistance)) {
if(oldHit.transform) {
// Add transparence
Color colorA = oldHit.transform.gameObject.renderer.material.color;
colorA.a = 1f;
oldHit.transform.gameObject.renderer.material.SetColor("_Color", colorA);
}
// Add transparence
Color colorB = hit.transform.gameObject.renderer.material.color;
colorB.a = 0.5f;
hit.transform.gameObject.renderer.material.SetColor("_Color", colorB);
// Save hit
oldHit = hit;
}
}
}
I did attach this script to my simple camera following the player. It might help you.
It can actually manage more than one obstructing the view and also you see I pass it a mask which I target with its name instead of checking for the collider name tag. Have fun.
using UnityEngine;
public class followPlayer : MonoBehaviour
{
public Transform player;
public Vector3 offset;
public Transform[] obstructions;
private int oldHitsNumber;
void Start()
{
oldHitsNumber = 0;
}
private void LateUpdate()
{
viewObstructed();
}
void Update()
{
transform.position = player.TransformPoint(offset);
transform.LookAt(player);
}
void viewObstructed()
{
float characterDistance = Vector3.Distance(transform.position, player.transform.position);
int layerNumber = LayerMask.NameToLayer("Walls");
int layerMask = 1 << layerNumber;
RaycastHit[] hits = Physics.RaycastAll(transform.position, player.position - transform.position, characterDistance, layerMask);
if (hits.Length > 0)
{ // Means that some stuff is blocking the view
int newHits = hits.Length - oldHitsNumber;
if (obstructions != null && obstructions.Length > 0 && newHits < 0)
{
// Repaint all the previous obstructions. Because some of the stuff might be not blocking anymore
for (int i = 0; i < obstructions.Length; i++)
{
obstructions[i].gameObject.GetComponent<MeshRenderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
}
}
obstructions = new Transform[hits.Length];
// Hide the current obstructions
for (int i = 0; i < hits.Length; i++)
{
Transform obstruction = hits[i].transform;
obstruction.gameObject.GetComponent<MeshRenderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.ShadowsOnly;
obstructions[i] = obstruction;
}
oldHitsNumber = hits.Length;
}
else
{ // Mean that no more stuff is blocking the view and sometimes all the stuff is not blocking as the same time
if (obstructions != null && obstructions.Length > 0)
{
for (int i = 0; i < obstructions.Length; i++)
{
obstructions[i].gameObject.GetComponent<MeshRenderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
}
oldHitsNumber = 0;
obstructions = null;
}
}
}
}