unity - mobile touch control to drag gameobject in 2D game - c#

I wanted to find out what is the best-practice method to use for touch controls for mobile 2D games, specifically dragging a paddle for breakout/arkanoid style games. Would be surprised if there are no easy built in Unity facility to do this as I would have thought this would have been a standard feature given the popularity of mobile games.
Any advice or links to tutorials, especially dragging game objects, would be greatly appreciated. Thanks.

My advice would be to use the unity UI features (introduced in Unity 4.6) using a script like this one :
public class DragControl : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
private bool m_Used;
private Vector3 m_MouseStartPosition;
private Vector3 m_ItemStartPosition;
public void Start()
{
m_Used = false;
}
public void Update()
{
if(m_Used)
{
GetComponent<RectTransform>().anchoredPosition = new Vector2(m_ItemStartPosition.x + (Input.mousePosition.x - m_MouseStartPosition.x), m_ItemStartPosition.y + (Input.mousePosition.y - m_MouseStartPosition.y));
// or you can set the y value to m_ItemStartPosition.y only if you don't want the paddle to move on Y axis
}
}
public void OnPointerDown(PointerEventData eventData)
{
m_Used = true;
m_MouseStartPosition = Input.mousePosition;
m_ItemStartPosition = GetComponent<RectTransform>().anchoredPosition;
}
public void OnPointerUp(PointerEventData eventData)
{
m_Used = false;
}
}
For this to work you have to enable the UI item raycasting (keep "Raycast Target" checked) and nothing blocking the raycast being over your item in the UI layers.
Also note that if you don't need it you can disable mutli touch input using Input.multiTouchEnabled = false;.

An easy solution would be to import the now standard (Unity 5?) "DragRigidbody" script and attach it to your Game Object. Just make sure your object has a RigidBody Component attached to it (which I would assume your paddle object would have already).

Related

Having problems with OnBecameVisible (sees through walls and is visible on scene view)

I'm trying to make a script that has an enemy chase the player only if the enemy is viewable by the player camera and the player hits the space key. I've been trying to use render.isVisible but run in to two MAJOR problems:
isVisible is enabled even if the enemy is visible in the scene view, making testing impossible.
isVisible works through walls, so if the player is looking at a wall and the enemy is behind it, it still registers as isVisible.
Please help I'm losing my mind. Thank you!
public NavMeshAgent enemy;
public Transform player;
Renderer m_Renderer;
private void Start()
{
m_Renderer = GetComponent<Renderer>();
}
private void OnBecomeInvisible()
{
enabled = false;
}
void OnBecameVisible()
{
enabled = true;
if (Input.GetKey(KeyCode.Space) && m_Renderer.isVisible)
{
Debug.Log("is visible");
enemy.SetDestination(player.position);
}
}
private void Update()
{
OnBecameVisible();
}
I'm honestly not sure what to try, thank you so much for any help!
IsVisible is really not designed for that usage. It just means object is being rendered by any camera. See the documentation here: https://docs.unity3d.com/ScriptReference/Renderer-isVisible.html
To determine whether something is actually visible from one entity by another, you typically have to do a frustum check followed by one or more raycasts to determine whether the target is blocked.

Activating animation when within radius

I am trying to create a script for my enemy turret, but it is not going well. I have a couple animations of the turret being activated and deactivated. What I need is that based on the distance from the player, it plays either animation. So once it moves inside the detection radius it plays the activation animation and once it is outside it plays the deactivation animation. Most of the other ways I try require me to create an Animation Controller, which I have little experience in using. I want a simple way to play one animation once it is inside and play a different one when it is outside. I think there was a way to store the animation clip in the script, and then play it. I have attached my current script, so you know what I mean.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyTurret : MonoBehaviour
{
public GameObject Player;
public float DistanceToPlayer;
public float DetectionRadius = 75;
// Start is called before the first frame update
void Start()
{
Player = GameObject.FindGameObjectWithTag("PlayerTank");
}
// Update is called once per frame
void Update()
{
DistanceToPlayer = Vector3.Distance(transform.position, Player.transform.position);
if (DistanceToPlayer<=DetectionRadius)
{
Debug.Log("Within Radius");
}
if (DistanceToPlayer >= DetectionRadius)
{
Debug.Log("Outside Radius");
}
}
}
Calculating and checking the distance of the player for every update() is not ideal. It will work, but it will do more work than it needs to when the player isn't even near it. Its not efficient.
What you may want to do if your player is a Rigidbody, is add a SphereCollider to the turret, set isTrigger=true, set the radius to be your detection radius, and handle the OnTriggerEnter() and OnTriggerExit() events to play or stop animations.
You can also add two public Animiation objects to the script, drag and drop your animations in the editor, then you can use animation.Play() and .Stop() etc. to control the animations.
Something similar to this. Not tested fully, but you can get the idea.
public float detectionRadius = 75;
public Animation activateAnimation;
public Animation deactivateAnimation;
void Start()
{
SphereCollider detectionSphere = gameObject.AddComponent<SphereCollider>();
detectionSphere.isTrigger = true;
detectionSphere.radius = detectionRadius;
detectionSphere.center = Vector3.zero;
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "PlayerTank")
{
activateAnimation.Play();
}
}
private void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "PlayerTank")
{
deactivateAnimation.Play();
}
}
Your animations must not loop otherwise you will have to add more logic to check if animation.isPlaying and do your own animation.Stop() etc.

Change color of individual prefab instance after it was touched Unity 2d

I started making a 2d game, where an Individual Prefab Instance has to change color after being touched. I'm new to programming in C# and I've scoured the internet for hours but I still can't find an answer which works. I tried adding GetComponent<Renderer>().material.color = Color.red; to the script on my prefab but it changes the color of all instantiated prefabs. My Unity version is 2019.2.3f1. Please help me.
Here's the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TouchDetector : MonoBehaviour
{
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
if (hit.transform.name == "square")
{
Destroy(gameObject);
print("TOUCHED");
GetComponent<Renderer>().material.color = Color.red;
}
}
}
}
}
Your problem lies in
GetComponent<Renderer>().material.color = Color.red;
Each of your instances of this script is running that method simultaneously so the Raycast and hit is processed on all of them.
In general it makes little sense to me that you do Destroy(gameObject) and right after it try to access GetComponent<Renderer> of the object you just destroyed ...
It is a bit unclear what you wanted to do but I guess it was something like
if (hit.transform.name == "square")
{
print("TOUCHED");
hit.gameObject.GetComponent<Renderer>().material.color = Color.red;
}
Which only colors the object that was hit. You would also only need exactly one instance of this component in your scene!
A way better way would be that each item detects the click itself! You can do this e.g. implementing the IPointerDownHandler interface like
public TouchDetector : MonoBehaviour, IPointerDownHandler
{
public void OnPointerDown(PointerEventData data)
{
Debug.Log("I was touched!", this);
GetComponemt<Renderer>().material.color = Color.Red;
}
}
This would go on each object that should detect a click/touch.
Notes:
In newer versions UI was moved to a Package in the PackageManager, make sure it is installed.
According objects either have to be UI or require a Collider and on the Camera a PhysicsRaycaster component.

Send player position to another player to move him in multi player game using Unity Photon

I follow the steps of PUN basic tutorial.
For now I reach to a part of what I want Send my position in the current time to another player with me in the room to move him. I can print my position each time I update it , what I need is to know how I can send position to another player to move him.
Let's say I have a desktop player and when I move him this translation moving the player on mobile.
And how I stop instantiate object on mobile, I just want to deal with the instantiated object on desktop.
I am using unity and Photon Network SDK.
Here is the code I used
using UnityEngine;
using System.Collections;
public class NetworkCharacter : Photon.PunBehaviour {
private Vector3 correctPlayerPos;
void Update()
{
if (!photonView.isMine)
transform.position = Vector3.Lerp(transform.position, this.correctPlayerPos, Time.deltaTime * 5);
photonView.RPC ("PrintPosition", PhotonTargets.All, transform.position);
}
void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.isWriting)
{
// We own this player: send the others our data
stream.SendNext(transform.position);
}
else
{
// Network player, receive data
this.correctPlayerPos = (Vector3)stream.ReceiveNext();
}
}
[PunRPC]
void PrintPosition(Vector3 pos)
{
Debug.Log (pos);
//I need to send position coordinates to the other device
}
}
The other class of establish multiplayer environment:
using UnityEngine;
using System.Collections;
public class NetworkManager : Photon.PunBehaviour {
// Use this for initialization
void Start () {
PhotonNetwork.ConnectUsingSettings ("0.1");
//PhotonNetwork.logLevel = PhotonLogLevel.Full;
}
void Update () {
}
void OnGUI()
{
GUILayout.Label(PhotonNetwork.connectionStateDetailed.ToString());
}
public override void OnJoinedLobby ()
{
Debug.Log("join lobby!");
PhotonNetwork.JoinRandomRoom ();
}
void OnPhotonRandomJoinFailed()
{
Debug.Log("Can't join random room!");
PhotonNetwork.CreateRoom(null);
}
void OnJoinedRoom()
{
Debug.Log("join random room!");
GameObject monster = PhotonNetwork.Instantiate("monsterprefab", Vector3.zero, Quaternion.identity, 0);
}
}
PUN has some new components since their new update.
I'd recommend you to use those components because it is really user friendly towards new users.
Some components you could use:
PhotonTransformView
PhotonRigidbodyView
PhotonAnimatorView
These three components will help you sync your position, physics and animations on the GameObject it is attached to.
If you do not want to use these components i suggest you search up: interpolation and extrapolation for Unity PUN.
Some good starting tutorials:
SkyArena PUN Tutorial This is a really good video tutorial[in parts] on PUN so you should definitely check that out.
Exit Games Marco Polo This tutorial is created by someone from PUN also really good to read even if you don't need it.
Hope this will help you.
-Menno
I found the answer in Demo Synchronization that provided in PUN plugin, it is really helpful.

Audio doesn't play when instantiate new object

I'm facing audio problems in my project. There are three paddle game objects and one cube. Cube has Rigidbody2d or BoxCollider2d. And there is also a button script attach to cube which has button function when we click on button cube Kinematic becomes false and drop on the paddle. When it's collide with any paddle cube destroy and instantiate again with new prefab. When Cube is falling sound is play and cube is destroy. New cube is instantiate when again click on button then error came.
MissingReferenceException: The object of type AudioSource has been destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object.
Scripts on all paddles:
public class Paddle : MonoBehaviour
{
[SerializeField]
private AudioSource source;
[SerializeField]
private AudioClip hit;
[SerializeField]
private BoxCollider2D collide;
[SerializeField]
private GameObject Clone;
void Awake()
{
collide.isTrigger = true;
}
void OnTriggerEnter2D(Collider2D target)
{
source.PlayOneShot(hit);
Destroy(target.gameObject);
Instantiate(Clone, new Vector3(0f, 4.51f, 0f), Quaternion.identity);
}
}
Cube Script:
public class Cube : MonoBehaviour
{
[SerializeField]
private AudioSource source;
[SerializeField]
private Rigidbody2D body;
[SerializeField]
private AudioClip play;
private Button bt;
private float pos;
public bool check;
void Start()
{
bt = GameObject.FindGameObjectWithTag("Button").GetComponent<Button>();
bt.onClick.AddListener(Fire);
body.isKinematic = true;
}
void Update()
{
if (check)
{
return;
}
Vector3 temp = transform.position;
pos = Camera.main.ScreenToWorldPoint(Input.mousePosition).x;
temp.x = Mathf.Clamp(pos, -6.25f, 6.25f);
body.position = temp;
}
public void Fire()
{
GameObject.FindGameObjectWithTag("Player").GetComponent<Cube>().check = true;
GameObject.FindGameObjectWithTag("Player").GetComponent<Rigidbody2D>().isKinematic = false;
source.PlayOneShot(play);
}
}
Cube image:
Paddles image:
New Export package:
https://drive.google.com/file/d/0B1H5fdK2PJAnSXVPdmE5Z3J1SUU
Problem in Video:
https://drive.google.com/file/d/0B1H5fdK2PJAnYzRfVnlQT1FyTlE
Your linked package works fine, it does play the sound every time I launch and destroy a cube.
Nonetheless, you should remove the method on destroy. Unity does not throw any error nor warning when a non-persistent listener is null, kinda weird but this is how it is. You should remove it manually:
void OnDestroy(){
bt.onClick.RemoveListener (Fire);
}
But your package does not throw any error when I run it.
Though, I would rethink your approach, instead of the cube assigning its Fire method to the Button event, I would have a script on the button containing the Fire method as well as the AudoiSource and clip. Then on Start, the Cube would pass itself so that the button could access its Cube and Rigidbody2D component.
Best would be to pass a class that contains those are members:
public class CubeArgument{
public readonly Rigidbody2D rg = null;
public readonly Cube cube = null;
public CubeArgument(Rigidbody2D rg, Cube cube){
this.rg = rg;
this.cube = cube;
}
}
then here goes your Cube start method:
void Start () {
bt = GameObject.FindGameObjectWithTag ("Button");
bt.GetComponent<ButtonController> ().Init(new CubeArgument(body, this));
body.isKinematic = true;
}
The ButtonController reference could even be made static since there is only one for the whole level.
and then on the button you have a ButtonController:
public class ButtonController : MonoBehaviour{
Cube currentCube = null;
Rigodbody2D currentRig = null;
public void Init(CubeArgument ca){
currentRig = ca.rg;
currentCube = ca.cube;
}
public void Fire(){
if(currentCube != null){ currentCube.check = true; }
if(currentRig != null) { currentRig.isKinematic = false; }
}
}
Fire is passed as listener to the Button onClick and this is it.
your problem can be from many different places. my guesses:
1)problem with your build: rebuild your code or export all of your package to new project and retest.
2)your target framework: latest version of unity(i have 5.1) just support to .net 3,5 and unity 4.x supports 2,5 i think. so chek your target framework to not to use functions that is not functional in your version
3)settings of your platform that your editor is running on: it can be volume of your platform to many other settings, first option to know that is run your project on other machine (maybe its a unity bug that part of code is not good for your hardware or driver on platform)
Edit: OP was using version 5.1.1. This error does not repeat after Unity 5.2.
Tried your package and it is working fine!
No errors. Audio played when instantiated.
Are you still facing errors? Weird, dude. Sounds like something is not clean enough.
Maybe there is some issue with your build.
Few things you could try:
1- Try restarting your Unity. Maybe this will force Clean things.
2- Create a new project and import your own package to test it!
3- I'm using Unity 5.3.0f4 what Unity version are you using? Try an update.
If none of above works, there is something wicked going on with your AudioSource reference and I can't help you with my actual knowledge. But still we can try to do different approaches for that.
Instead of physically referenced it (Dragging and dropping), do it at the start of your script.
Desperate approach 1
On Cube.cs:
First remove [SerializeField] located above of private AudioSource source; and add the following line on the Start() method:
source = gameObject.GetComponent<AudioSource> ();
Practically the same, but now the script is referencing the own object Audio Source for you. Do the same approach with Paddle.cs. [remember to check the audio name when applying the approach for Paddle script. On paddle the audio name is PlayOneShot (hit) not PlayOneShot (play)].
If this still trigger some error. Let's try another approach.
Desperate approach 2
On Cube.cs:
Remove/Comment the following line on Fire() method:
source.PlayOneShot (play);
Add the following line to Fire() method:
gameObject.GetComponent<AudioSource> ().PlayOneShot (play);
This will get your actual AudioSource on the go. Do the same approach on Paddle.cs [remember to check the audio name when applying the approach for Paddle script. On paddle the audio name is PlayOneShot (hit) not PlayOneShot (play)].

Categories