So, I have the following code for switching weapons inside a placeholder and I want to extend it, to switching weapons from a button too, so I will be able to switch them on my mobile too.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class WeaponSwitcher : MonoBehaviour {
public int selectedWeapon = 0;
// Use this for initialization
void Start () {
SelectWeapon();
}
// Update is called once per frame
void Update () {
int previousSelectedWeapon = selectedWeapon;
if (Input.GetAxis ("Mouse ScrollWheel") > 0f)
{
if (selectedWeapon >= transform.childCount - 1)
selectedWeapon = 0;
else
selectedWeapon++;
}
if (Input.GetAxis ("Mouse ScrollWheel") < 0f)
{
if (selectedWeapon <= 0)
selectedWeapon = transform.childCount - 1;
else
selectedWeapon--;
}
if (previousSelectedWeapon != selectedWeapon)
{
SelectWeapon ();
}
}
void SelectWeapon()
{
int i = 0;
foreach (Transform weapon in transform)
{
if (i == selectedWeapon)
weapon.gameObject.SetActive (true);
else
weapon.gameObject.SetActive (false);
i++;
}
}
}
What should I do? I'm newbie and everything I have tried didn't work :/
I have tried with an independent script just for the button to looking for which weapon is active and deactivate the other (there just two) etc but it didn't work.
public class ChangeWeapon : MonoBehaviour
{
public GameObject gun;
public GameObject shotgun;
void switchWeapons ()
{
gun = GameObject.Find("Gun");
shotgun = GameObject.Find("Shotgun");
if (gun.gameObject.activeSelf)
{
shotgun.gameObject.SetActive (true);
gun.gameObject.SetActive (false);
}
else
{
shotgun.gameObject.SetActive (false);
gun.gameObject.SetActive (true);
}
}
}
I found a really simple solution that works.
I placed two buttons on top of another, I disabled the second one and on click actions I do the followings:
Enable the second button
Disable the first button
Disable the first gun
Enable the second gun
And vise versa.
Image
Related
I am working on a small game for a school project, in which my player needs to attack enemies in a level. My plan is to have a collider that is enabled in an attached script, and then disabled when the attack is done. My current problem is that that the collider does not flip the way it is supposed to, it seems to flip directly on the overall x axis instead of flipping in the x axis related to the player. It is a child of the player so I am clueless as to why it is doing this. Any solutions or other approaches would be greatly appreciated. I will attach the current script that controls the collider below.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VerticalSword : MonoBehaviour
{
//Variables use to active the colliders
Collider2D AttackColliderVertical;
//Variables for the location of the collider
Vector2 attackOffset;
private void Start()
{
AttackColliderVertical = GetComponent<Collider2D>();
attackOffset = transform.position;
AttackColliderVertical.enabled = false;
}
public void FixedUpdate()
{
attackOffset = transform.position;
}
public void AttackUp()
{
AttackColliderVertical.enabled = true;
if (attackOffset.y > 0)
{
transform.position = attackOffset;
}
else if (attackOffset.y < 0)
{
transform.position = new Vector2(attackOffset.x, (attackOffset.y * -1)); //I think the problem is somewhere in this if and else if statement
}
print("Attack up successful"); //Error checking (This works when run)
}
public void AttackDown()
{
AttackColliderVertical.enabled = true;
if (attackOffset.y > 0)
{
transform.position = new Vector2(attackOffset.x, (attackOffset.y * -1));
}
else if (attackOffset.y < 0)
{
transform.position = attackOffset; //I think the problem is somewhere in this if and else if statement
}
print("Attack down successful"); //Error checking (This works when run)
}
public void StopAttack()
{
AttackColliderVertical.enabled = false;
}
}
Use transform.localPosition, not transform.position (that's its world space position). You need to change it everywhere in this script; the Start() function and the two attack functions
So I am using two push buttons (connected to an Arduino Uno) as an input to my game. The player has to push down both buttons at the same time for the character to move in the game. I want the player to hold down the buttons for a different amount of time in each level. I have a working Arduino and a working Unity timer and player script, but am not able to get the code to do what I want. What I basically want is that only when the player presses the buttons down, does the timer start counting down. Right now, the timer starts as soon as the scene begins. I know that I somehow have to reference the timer script to the button object, I have tried this but it still doesn't work. Note that the timer UI does have a Timer tag on it. I have also referenced the Player Controller script in the Timer script. Right now, Its giving me a range of errors. I have attached an image depicting these errors.error image
The Timer script:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class Timer : MonoBehaviour
{
//int startTime = 0;
public bool buttonPressed = false;
public int timeLeft;
public Text countdownText;
GameObject Character;
void Awake()
{
Character = GameObject.FindWithTag("Player");
}
public void Start()
{
//StartCoroutine("LoseTime");
BeginTimer();
}
void Update()
{
countdownText.text = ("Time Left = " + timeLeft);
if (timeLeft <= 0)
{
//StopCoroutine("LoseTime");
//countdownText.text = "Times Up!";
Invoke("ChangeLevel", 0.1f);
}
}
public void BeginTimer()
{
Character.GetComponent<PlayerController>().Update();
//gameObject.GetComponent<MyScript2>().MyFunction();
if (buttonPressed == true )
{
StartCoroutine("LoseTime");
}
else if (buttonPressed == false)
{
StopCoroutine("LoseTime");
}
}
IEnumerator LoseTime()
{
while (true)
{
yield return new WaitForSeconds(1);
timeLeft--;
}
}
void ChangeLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
The Player Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO.Ports;
public class PlayerController : MonoBehaviour
{
SerialPort sp = new SerialPort("\\\\.\\COM4", 9600);
//player == GameObject.FindWithTag("Player").GetComponent<>();
public float Speed;
public Vector2 height;
public float xMin, xMax, yMin, yMax;
public bool buttonPressed = false;
GameObject Character;
public void Awake()
{
Character = GameObject.FindWithTag("Player");
}
public void Start()
{
if (!sp.IsOpen)
{ // If the erial port is not open
sp.Open(); // Open
}
sp.ReadTimeout = 1; // Timeout for reading
}
public void Update()
{
if (sp.IsOpen)
{ // Check to see if the serial port is open
try
{
string value = sp.ReadLine();//To("Button"); //Read the information
int button = int.Parse(value);
//float amount = float.Parse(value);
//transform.Translate(Speed * Time.deltaTime, 0f, 0f); //walk
if (button == 0) //*Input.GetKeyDown(KeyCode.Space*/) //jump
{
buttonPressed = true;
Character.GetComponent<Rigidbody2D>().AddForce(height, ForceMode2D.Impulse);
Character.GetComponent<Rigidbody2D>().position = new Vector3
(
Mathf.Clamp(GetComponent<Rigidbody2D>().position.x, xMin, xMax),
Mathf.Clamp(GetComponent<Rigidbody2D>().position.y, yMin, yMax)
);
Timer tmr = GameObject.Find("Timer").GetComponent<Timer>();
tmr.BeginTimer();
}
}
catch (System.Exception)
{
}
}
void ApplicationQuit()
{
if (sp != null)
{
{
sp.Close();
}
}
}
}
}
I think the problem may be with how I am referencing the scripts in each other.
In your timer you have a quite strange mixup of Update and Coroutine. Also note that BeginTimer is called exactly once! You also shouldn't "manually" call Update of another component.
I wouldn't use Update at all here. Simply start and stop a Coroutine.
The Timer script should only do the countdown. It doesn't have to know more:
public class Timer : MonoBehaviour
{
public int timeLeft;
public Text countdownText;
private bool timerStarted;
public void BeginTimer(int seconds)
{
// Here you have to decide whether you want to restart a timer
timeLeft = seconds;
// or if you rather want to continue counting down
//if(!timerStarted) timeLeft = seconds;
StartCoroutine(LoseTime());
}
public void StopTimer()
{
StopAllCoroutines();
}
private IEnumerator LoseTime()
{
timerStarted = true;
while (timeLeft > 0)
{
yield return new WaitForSeconds(1);
timeLeft --;
countdownText.text = $"Time Left = {timeLeft}";
}
// Only reached after the timer finished and wasn't interrupted meanwhile
// Using Invoke here is a very good idea since we don't want to interrupt anymore
// if the user lets go of the button(s) now
Invoke(nameof(ChangeLevel), 0.1f);
}
void ChangeLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
In general avoid to use Find at all. If anyhow possible already reference things in the Inspector! If needed you can use Find but only once! What you never want to do is use any of the Find and GetComponent variants repeatedly - rather store the reference the first time and re-use it - and especially not in Update no a per frame basis. They are very expensive calls!
public class PlayerController : MonoBehaviour
{
public float Speed;
public Vector2 height;
// I prefer to use Vector2 for such things
public Vector2 Min;
public Vector2 Max;
public bool buttonPressed = false;
// Already reference these via the Inspector if possible!
public Rigidbody2D Character;
public Timer timer;
public Rigidbody2D _rigidbody;
private SerialPort sp = new SerialPort("\\\\.\\COM4", 9600);
private void Awake()
{
FetchReferences();
}
// This is my "secret" tip for you! Go to the component in the Inspector
// open the ContextMenu and hit FetchReferences
// This already stores the references in the according fields ;)
[ContextMenu("FetchReferences")]
private void FetchReferences()
{
if(!Character)Character = GameObject.FindWithTag("Player"). GetComponent<Rigidbody2D>();
if(!timer) timer = GameObject.Find("Timer").GetComponent<Timer>();
}
private void Start()
{
if (!sp.IsOpen)
{
sp.Open(); // Open
}
sp.ReadTimeout = 1;
}
private void Update()
{
// I wouldn't do the serialport open check here
// your if block simply silently hides the fact that your application
// doesn't work correctly! Rather throw an error!
try
{
string value = sp.ReadLine(); //Read the information
int button = int.Parse(value);
//TODO: Since it isn't clear in your question how you get TWO buttons
//TODO: You will have to change this condition in order to only fire if both
//TODO: buttons are currently pressed!
buttonPressed = button == 0;
if (buttonPressed)
{
Character.AddForce(height, ForceMode2D.Impulse);
// The clamping of a rigidbody should always be done ine FixedUpdate!
// Pass in how many seconds as parameter or make the method
// parameterless and configure a fixed duration via the Inspector of the Timer
timer.BeginTimer(3.0f);
}
else
{
// Maybe stop the timer if condition is not fulfilled ?
timer.StopTimer();
}
}
catch (System.Exception)
{
// You should do something here! At least a Log ...
}
}
private void FixedUpdate()
{
// Here I wasn't sure: Are there actually two different
// Rigidbody2D involved? I would assume you rather wanted to use the Character rigidbody again!
Character.position = new Vector3(Mathf.Clamp(Character.position.x, Min.x, Max.x), Mathf.Clamp(Character.position.y, Min.y, Max.y));
}
// Did you mean OnApplicationQuit here?
private void ApplicationQuit()
{
if (sp != null)
{
{
sp.Close();
}
}
}
}
Typed on smartphone but I hope the idea gets clear
The problem is that I what to create an openable door. This door should open when the player enter the Box Collider which is connected to the door. But the problem is when the door begins to open and to rotate, Collider starts to rotate too which brings me a lot of problems with usind such an idea. I try to create EmptyObject with its Collider but I can't connect this Collider with script and OnTriggerEnter function itself. Maybe I don't understand something, who knows, I'm just a begginer. How knows how to help, please write an answer.
My code if somebody needs it:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class openDoor : MonoBehaviour {
public Vector3 Rotation_;
private int i;
public float speed;
bool opentheDoor;
bool closetheDoor;
// Use this for initialization
void Start () {
opentheDoor = false;
closetheDoor = false;
}
// Update is called once per frame
void Update () {
if (opentheDoor == true) {
this.transform.Rotate (Rotation_ * Time.deltaTime * speed);
i += 1;
if (i == 70) {
opentheDoor = false;
i = 0;
}
}
if (closetheDoor == true) {
this.transform.Rotate (-Rotation_ * Time.deltaTime * speed);
i += 1;
if (i == 70) {
closetheDoor = false;
i = 0;
}
}
}
void OnTriggerEnter (Collider other) {
if (other.gameObject.tag == "Player") { {
opentheDoor = true;
}
}
}
void OnTriggerExit (Collider other) {
if (other.gameObject.tag == "Player") {
closetheDoor = true;
}
}
}
This is how i would handle the scenerio
Take
DoorHandler.cs
public class DoorHandler : MonoBehaviour {
public Door door;
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
door.OpenDoor();
}
}
}
This should be attached to Parent of the door.
Next Take
Door.cs
public class Door : MonoBehaviour {
public bool isOpened = false;
public void OpenDoor()
{
if (!isOpened)
{
isOpened = true;
Debug.Log("OPEN");
//OPEN DOOR CODE!
}
}
}
Attach this to the Door GameObject
NOTE
The hierarchy would be like DoorHandler->Door->DoorModel (where Door is just an empty gameobject pivot of the Door)
In DoorHandler GameObject attach BoxCollider and Check Mark IsTrigger.
Also Player SHOULD HAVE A RIGIDBODY (preferably Kinametic) and obviously a collider
So When Player enters the DoorHandler's Collider -> The DoorHandler's OnTriggerEnter will be triggered and finally Call the Door to OpenDoor()
Add another check in OnTriggerEnter that checks if the door is currently opening or not.
void OnTriggerEnter (Collider other) {
if (other.gameObject.tag == "Player" && !opentheDoor) {
opentheDoor = true;
}
}
attach the door to an empty object. put the trigger on the empty object. then make the ontrigger entry rotate the door, not the paent object, and the collider will remain in place.
Parent
-child(door)
-child(collider)
Ive a question about how to change spirte images during runtime for a bunch of objects.
So i made a tiny racer 2d game, and therefore you can choose differend themes. You have this option in an integraded menu (not a seperate scene).
My question:
Can i switch the sprites easy during the runtime? Ive made prefabs for each track element - and i changed the sprites of those prefabs, but the change only gets visible, after the scene is reloaded. So i would need to avoid this.
Has someone a solution or a hint how i could do that?
Thanks in advance!
Code:
public class Background_Controller : MonoBehaviour {
public Camera mainCamera;
public Color colorNormal;
public GameObject[] Prefabs;
public Sprite[] normalSprites;
public Sprite[] tronSprites;
// Use this for initialization
void Awake () {
SwitchBackgroundFunction();
}
public void SwitchBackground(string Theme)
{
switch(Theme)
{
case "GreenHell":
PlayerPrefs.SetString("Theme", "Normal");
break;
case "NeonCity":
PlayerPrefs.SetString("Theme", "Tron");
break;
}
SwitchBackgroundFunction();
}
private void SwitchBackgroundFunction()
{
int prefabCount = Prefabs.Length;
if (PlayerPrefs.GetString("Theme") == "Normal")
{
mainCamera.backgroundColor = colorNormal;
for (int i = 0; i <= prefabCount - 1; i++)
{
Prefabs[i].GetComponent<SpriteRenderer>().sprite = normalSprites[i];
}
}
if (PlayerPrefs.GetString("Theme") == "Tron")
{
mainCamera.backgroundColor = Color.black;
for (int i = 0; i <= prefabCount - 1; i++)
{
Prefabs[i].GetComponent<SpriteRenderer>().sprite = tronSprites[i];
}
}
}
// Update is called once per frame
void Update () {
}
}
You can do something along the following lines to swap in a sprite from within your resources folder during runtime.
Sprite spr;
spr = Resources.Load<Sprite>("mysprite"); //insert name and file path to sprite within Resources folder
GetComponent<SpriteRenderer>().sprite = spr;
i am not sure how to do this ,my goal is to create a button that will control the player on the plane for the mobile touch devices.I created the button I am trying to make it to control the player but i am failing here is my player c# script
using UnityEngine;
using System.Collections;
public class PlayerMovement : MonoBehaviour
{
public float speed = 500f, jumpHeight = 500f;
Transform myTrans;
Rigidbody2D myBody;
Vector2 movement;//used as temp holder variable before applying to velocity
bool isGrounded = true;
void Start ()
{
myTrans = this.transform;
myBody = this.GetComponent<Rigidbody2D>() ;
}
void Update ()
{
//Check if the player is grounded or not
isGrounded = Physics2D.Linecast(myTrans.position,
GameObject.Find(this.name+"/tag_ground").transform.position,
LayerMask.NameToLayer("Player"));
//Just for visualization, not needed for gameplay
Debug.DrawLine (myTrans.position,
GameObject.Find(this.name+"/tag_ground").transform.position,
Color.red);
//Works for keyboards and joysticks
#if !UNITY_ANDROID && !UNITY_IPHONE && !UNITY_BLACKBERRY && !UNITY_WINRT
Move (Input.GetAxisRaw("Horizontal"));
if (Input.GetButtonDown ("Jump"))
Jump ();
#endif
}
//
//Separate Move and Jump so they can be called externally by TouchButtons
//
public void Move(float horizontal_input)
{
movement = myBody.velocity;
if(isGrounded)//we can only move left and right if on the ground
movement.x = horizontal_input * speed * Time.deltaTime;
//Apply the movement to the player
myBody.velocity = movement;
}
public void Jump()//we can only jump if on the ground
{
if(isGrounded)
myBody.velocity += jumpHeight * Vector2.up * Time.deltaTime;
}
}
here is the touch logic script
using UnityEngine;
using System.Collections;
public class TouchLogicV2 : MonoBehaviour
{
public static int currTouch = 0;//so other scripts can know what touch is currently on screen
[HideInInspector]
public int touch2Watch = 64;
private new GUITexture guiTexture;
public virtual void Update()//If your child class uses Update, you must call base.Update(); to get this functionality
{
//is there a touch on screen?
if(Input.touches.Length <= 0)
{
OnNoTouches();
}
else //if there is a touch
{
//loop through all the the touches on screen
for(int i = 0; i < Input.touchCount; i++)
{
currTouch = i;
//executes this code for current touch (i) on screen
if(this.guiTexture != null && (this.guiTexture.HitTest(Input.GetTouch(i).position)))
{
//if current touch hits our guitexture, run this code
if(Input.GetTouch(i).phase == TouchPhase.Began)
{
OnTouchBegan();
touch2Watch = currTouch;
}
if(Input.GetTouch(i).phase == TouchPhase.Ended)
{
OnTouchEnded();
}
if(Input.GetTouch(i).phase == TouchPhase.Moved)
{
OnTouchMoved();
}
if(Input.GetTouch(i).phase == TouchPhase.Stationary)
{
OnTouchStayed();
}
}
//outside so it doesn't require the touch to be over the guitexture
switch(Input.GetTouch(i).phase)
{
case TouchPhase.Began:
OnTouchBeganAnywhere();
break;
case TouchPhase.Ended:
OnTouchEndedAnywhere();
break;
case TouchPhase.Moved:
OnTouchMovedAnywhere();
break;
case TouchPhase.Stationary:
OnTouchStayedAnywhere();
break;
}
}
}
}
//the default functions, define what will happen if the child doesn't override these functions
public virtual void OnNoTouches(){}
public virtual void OnTouchBegan(){print (name + " is not using OnTouchBegan");}
public virtual void OnTouchEnded(){}
public virtual void OnTouchMoved(){}
public virtual void OnTouchStayed(){}
public virtual void OnTouchBeganAnywhere(){}
public virtual void OnTouchEndedAnywhere(){}
public virtual void OnTouchMovedAnywhere(){}
public virtual void OnTouchStayedAnywhere(){}
}
I am following the touchlogic tuts but am struggling to move the player on mobile.Any with good examples on touch input is welcomed to give me the links for examples.thanks