hololens 101 chapter 3: what is "=>" for? - c#

Hololense tutorial here
in chapter 3 GazeGestureManager.cs:
using UnityEngine;
using UnityEngine.VR.WSA.Input;
public class GazeGestureManager : MonoBehaviour
{
public static GazeGestureManager Instance { get; private set; }
// Represents the hologram that is currently being gazed at.
public GameObject FocusedObject { get; private set; }
GestureRecognizer recognizer;
// Use this for initialization
void Start()
{
Instance = this;
// Set up a GestureRecognizer to detect Select gestures.
recognizer = new GestureRecognizer();
recognizer.TappedEvent += (source, tapCount, ray) =>
{
// Send an OnSelect message to the focused object and its ancestors.
if (FocusedObject != null)
{
FocusedObject.SendMessageUpwards("OnSelect");
}
};
recognizer.StartCapturingGestures();
}
// Update is called once per frame
void Update()
{
// Figure out which hologram is focused this frame.
GameObject oldFocusObject = FocusedObject;
// Do a raycast into the world based on the user's
// head position and orientation.
var headPosition = Camera.main.transform.position;
var gazeDirection = Camera.main.transform.forward;
RaycastHit hitInfo;
if (Physics.Raycast(headPosition, gazeDirection, out hitInfo))
{
// If the raycast hit a hologram, use that as the focused object.
FocusedObject = hitInfo.collider.gameObject;
}
else
{
// If the raycast did not hit a hologram, clear the focused object.
FocusedObject = null;
}
// If the focused object changed this frame,
// start detecting fresh gestures again.
if (FocusedObject != oldFocusObject)
{
recognizer.CancelGestures();
recognizer.StartCapturingGestures();
}
}
}
I really don't understand this line:
recognizer.TappedEvent += (source, tapCount, ray) =>
What is inside (), why there is a => operator and what is for?

Take a look at C# Lambda operator here. It separates the block from the input variables on the left. So it looks like that the block will be executed on the tapped event and the parameters (source, tapCount, ray) will be passed and can be used in the block.

I like to think of it as a "inlined delegate to handle events or callbacks". Hope that helps :)

Related

Unity InputSystem value constantly outputs 0 unless its read from a perform event

So I am trying to create a new first-person movement system with the new input system in order to make gamepad support so much easier and I am experiencing a problem when I try to read the value of the Vector2 in a FixedUpdate loop, it only outputs (0,0) but if I read it in an InputAction.performed event it works. However, I cannot use the event as it doesn't repeat on keyboard input and it isn't smooth. I've seen a tutorial linked here and at the end it does demonstrate you can pull information from outside events. Now my question is did I miss something or is there a different way to do it, my code is found below
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Assertions;
public class MovementEngine : MonoBehaviour
{
public InputMaster Input; // My input device here
public float currentSpeed = 2f;
public float walkingSpeed = 2f;
public float runningSpeed = 4f;
public Transform cameraTransform;
public CharacterController controller;
public InputAction Movement;
// Start is called before the first frame update
void Awake()
{
Input = new InputMaster(); // Creates new instance
}
void OnEnable()
{
Movement = Input.Player.Movement;
Movement.Enable(); // it is enabled
Input.Player.Interaction.performed += Interact;
Input.Player.Interaction.Enable();
}
private void Interact(InputAction.CallbackContext context)
{
Debug.Log("Interact");
}
void OnDisable()
{
Movement.Disable();
Input.Player.Interaction.Disable();
}
// Update is called once per frame
void FixedUpdate(){
Debug.Log("Movement: " + Input.Player.Movement.ReadValue<Vector2>()); // doesn't work
}
}
Store the value (retrieved in the performed event) in a variable, and use that variable in fixed update.
Make sure to reset the variable from the cancelled event (otherwise the variable will hold the last retrieved value from performed event).
You can read the input value directly into a class variable, as shown below.
// Value read from user input
//
private Vector2 movement;
private void Start()
{
SubscribeEvents();
}
private void SubscribeEvents()
{
// Read the value directly into our movement variable
//
Input.Player.Movement.performed += ctx => movement = ctx.ReadValue<Vector2>();
// Reset the movement variable in cancelled since we are no longer interacting
//
Input.Player.Movement.cancelled += ctx => movement = Vector2.zero;
}
private void FixedUpdate()
{
Debug.Log("Movement: " + movement);
}
I fixed this a while ago but StackOverflow never posted my answer
I fixed it by downgrading to Unity InputSystem v1.0.0 as v1.1.1 seemed to not like the InputAction.ReadValue<>() function.

How to call a method after each line of code or something like that

I have a method that is supposed to check a player's HP and then perform some logic based on if the number is greater than 0, or less than or equal to 0.
The method works but if I type it in the code and then change the hp value of a player it won't do anything until I type in the method name again. Then it will display the correct information.
At first I thought of using some kind of loop instead of a method. If I am correct, that means I'd have to have put curly braces around all my code that needs to get checked (which means basically around the whole code and I don't want that). Same with IF statement - even if I put it at the beginning of the code I'd still have to put curly braces around all the code.
Then I thought of a method which I already mentioned. And as I said - it does what it's supposed to do but only if I paste it multiple times into the main code.
Now, my question - is there ANY way to make the method "repeat itself" constantly or, I don't know, start at some place in the code and remain active?
Here is the method:
static void Status()
{
if (Player.playerHealth <= 0)
{
Player.isDead = true;
Console.WriteLine("You are dead!");
}
else if(Player.playerHealth > 0)
{
Player.isDead = false;
Console.WriteLine("You are not dead!");
}
}
I would be really grateful for any help.
You can define playerHealth as a property. That way, any time anyone changes it, you can make some code fire, including the check that you want.
class Player
{
protected int _playerHealth = 0;
public int PlayerHealth
{
set
{
_playerHealth = value;
if (_playerHealth == 0)
{
isDead = true;
Console.WriteLine("You are dead!");
}
}
get
{
return _playerHealth;
}
}
Now you don't even need to call Status... the logic will occur automatically whenever the player's health is modified.
var player = new Player();
player.PlayerHealth = 0; //Automatically triggers message
Correct me if I'm wrong but you want to call a method everytime the player's life changes? It looks like you should use an event.
public class HealthEventArgs : EventArgs
{
public int Health { get; set; }
public HealthEventArgs(int health)
: base()
{
this.Health = health;
}
}
public class Player
{
public event EventHandler<HealthEventArgs> LifeChanged;
int _Health;
public int Health
{
get => _Health;
set
{
_Health = value;
LifeChanged?.Invoke(this, new HealthEventArgs(_Health));
}
}
}
Then in your code it would look something like that
Player player = new Player();
player.LifeChanged += (o, e) =>
{
Player p = o as Player;
int health = e.Health;
// Whatever logic here, with access to the player and its health
};
With that mechanism in place, everytime the health of a player changes the event will fire and you will have access to the player and its health and can act accordingly.
If you're not confortable with lambda expressions, the code above can also be written as such
Player player = new Player();
player.LifeChanged += PlayerLifeChanged;
public void PlayerLifeChanged(object o, HealthEventArgs e)
{
Player p = o as Player;
int health = e.Health;
// Whatever logic here, with access to the player and its health
};

Adjust the length of animation clip to match cooldown

I'm planning on have several buttons to spawn monsters, all with a different cooldown. Is there a way to match the animation clip to be the same length as the cooldown of the button?
Here is an example: https://gyazo.com/0a2ae868e5458c701e1a258aac6dc59a
The animation is 1 second but the cooldown is 3 seconds.
Here is my code:
private void ButtonCooldown()
{
if (GetComponent<Button>().interactable == false)
{
buttonTimer += Time.deltaTime;
if (buttonTimer >= cooldown)
{
GetComponent<Button>().interactable = true;
buttonTimer = 0;
}
}
}
public void DisableButton()
{
GetComponent<Button>().interactable = false;
myAnimatior.SetTrigger("ButtonCooldownAnimation");
}
You could adjust the according Animator's speed to adjust it's overall playback speed.
E.g. something like
// Adjust in the inspector
[SerializeField] private float cooldownTime = 3;
// Already reference this via the Inspector if possible
[SerializeField] private Button button;
private void Awake ()
{
// Do this only once!
if(!button) button = GetComponemt<Button>();
}
public void DisableButton()
{
button.interactable = false;
// typo in Animator btw ;)
myAnimatior.SetTrigger("ButtonCooldownAnimation");
// Make the Animator play slower so the animation now takes 3 seconds
myAnimatior.speed = 1/cooldownTime;
// Instead of Update simply use Invoke here
// Execute the method called WhenCooldownDone after cooldownTime seconds
Invoke(nameof(WhenCooldownDone), cooldownTime);
}
private void WhenCooldownDone ()
{
button.interactable = true;
myAnimator.speed = 1;
}
As in the comments I would use Invoke instead of constantly checking the states in Update. In particular never use GetComponentrepeatedly in Update. It is very expensive. Always try to rather store the reference and reuse it.

Is there a way to start a method from button click without using Update() function in unity

Below is my C# script. I added a button to my project with a On Click event and called the Rotate() method. But for some reason it is not working
using System.Threading;
using UnityEngine;
public class Orbit : MonoBehaviour {
public GameObject sun;
public float speed;
// Use this for initialization
void Start () {
}
public void Update()
{
Rotate();
}
public void Rotate()
{
transform.RotateAround(sun.transform.position, Vector3.up, speed *
Time.deltaTime);
}
}
I commented the Update() method when calling the Rotate() method. I also created a game object for the script.
The reason why it only works in Update currently is that
public void Rotate()
{
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
}
needs to be called repeatetly. Otherwise it will only rotate for exactly one frame and cause of Time.deltaTime only a very small amount. But the onClick event of the Button component is fired only once. It is similar to e.g. Input.GetKeyDown which is only called once when the key goes down. There is no implementation in the Button component itslef to handle a continued button press.
What you want instead as far as I understand is rotating the object after the button click
start to rotate for ever
for a certain duration
until you press the button again
until it is released (-> implement a continuesly firing button see below)
The Button component alone can only do the first three:
Rotate for ever
Either using a Coroutine
private bool isRotating;
public void Rotate()
{
// if aready rotating do nothing
if(isRotating) return;
// start the rotation
StartCoroutine(RotateRoutine());
isRotating = true;
}
private IEnumerator RotateRoutine()
{
// whuut?!
// Don't worry coroutines work a bit different
// the yield return handles that .. never forget it though ;)
while(true)
{
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// leave here, render the frame and continue in the next frame
yield return null;
}
}
or still doing it in Update
private bool isRotating = false;
private void Update()
{
// if not rotating do nothing
if(!isRotating) return;
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
}
public void Rotate()
{
// enable the rotation
isRotating = true;
}
Note that the Update solution is only for your understanding what is happening. It should not be used like that because it is not that efficient since Update is called continously and checks the bool also if not rotating yet. That produces unnecessary overhead. The same applies to all following examples: Prefere to use the Coroutines over Update (In this case! In other cases it is actuall better and more efficient to use one Update method instead of multiple concurrent Coroutines .. but that's another story.)
Rotate for a certain duration
As Coroutine
// adjust in the inspector
// how long should rotation carry on (in seconds)?
public float duration = 1;
private bool isAlreadyRotating;
public void Rotate()
{
// if aready rotating do nothing
if(isAlreadyRotating) return;
// start a rottaion
StartCoroutine(RotateRoutine());
}
private IEnumerator RotateRoutine()
{
// set the flag to prevent multiple callse
isAlreadyRotating = true;
float timePassed = 0.0f;
while(timePassed < duration)
{
// rotate a small amount
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// add the time passed since last frame
timePassed += Time.deltaTime;
// leave here, render the frame and continue in the next frame
yield return null;
}
// reset the flag so another rotation might be started again
isAlreadyRotating = false;
}
or in Update
public float duration;
private bool isRotating;
private float timer;
private void Update()
{
// if not rotating do nothing
if(!isRotating) return;
// reduce the timer by passed time since last frame
timer -= Time.deltaTime;
// rotate a small amount
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// if the timer is not 0 return
if(timer > 0) return;
// stop rottaing
isRotating = false;
}
public void Rotate()
{
// if already rotating do nothing
if(isRotating) return;
// start rotating
isRotating = true;
// enable timer
timer = duration;
}
Toggle rotation
This is very similar to the one before but this time instead of the timer you stop the rotation by clicking again. (You even could combine the two but than be carefull to reset the isRotating flag correctly ;) )
As Coroutine
private bool isRotating;
public void ToggleRotation()
{
// if rotating stop the routine otherwise start one
if(isRotating)
{
StopCoroutine(RotateRoutine());
isRotating = false;
}
else
{
StartCoroutine(RotateRoutine());
isRotating = true;
}
}
private IEnumerator RotateRoutine()
{
// whuut?!
// Don't worry coroutines work a bit different
// the yield return handles that .. never forget it though ;)
while(true)
{
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// leave here, render the frame and continue in the next frame
yield return null;
}
}
or as Update
private bool isRotating;
private void Update()
{
// if not rotating do nothing
if(!isRottaing) return;
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
}
public void ToggleRotation()
{
// toggle the flag
isRotating = !isRotating;
}
Rotate until released
This is the most "complicated" part since the Button alone can not accomplish this (there is no "on Release"). But you can implement this using IPointerXHandler interfaces.
The good news: You can keep your original script as you have it currently
public void Rotate()
{
transform.RotateAround(sun.transform.position, Vector3.up, speed *
Time.deltaTime);
}
Now you need an extension for the button. It will call the whilePressed event repeatedly every frame like Update so you just have to reference your Rotate method in whilePressed instead of the onClick.
Again there are two options either implementing it as a Coroutine:
[RequireComponent(typeof(Button))]
public class HoldableButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler
{
// reference the same way as in onClick
public UnityEvent whilePressed;
private Button button;
private bool isPressed;
private void Awake()
{
button = GetComponent<Button>();
if(!button)
{
Debug.LogError("Oh no no Button component on this object :O",this);
}
}
// Handle pointer down
public void OnPointerDown()
{
// skip if the button is not interactable
if(!button.enabled || !button.interactable) return;
// skip if already rotating
if(isPressed) return;
StartCoroutine(PressedRoutine());
isPressed= true;
}
// Handle pointer up
public void OnPointerUp()
{
isPressed= false;
}
// Handle pointer exit
public void OnPointerExit()
{
isPressed= false;
}
private IEnumerator RotateRoutine()
{
// repeatedly call whilePressed until button isPressed turns false
while(isPressed)
{
// break the routine if button was disabled meanwhile
if(!button.enabled || !button.interactable)
{
isPressed = false;
yield break;
}
// call whatever is referenced in whilePressed;
whilePressed.Invoke();
// leave here, render the frame and continue in the next frame
yield return null;
}
}
}
or you could do the same in Update again as well
[RequireComponent(typeof(Button))]
public class HoldableButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler
{
public UnityEvent whilePressed;
private bool isPressed;
private Button button;
private void Awake()
{
button = GetComponent<Button>();
if(!button)
{
Debug.LogError("Oh no no Button component on this object :O",this);
}
}
private void Update()
{
// if button is not interactable do nothing
if(!button.enabled || !button.interactable) return;
// if not rotating do nothing
if(!isPressed) return;
// call whatever is referenced in whilePressed;
whilePressed.Invoke();
}
// Handle pointer down
public void OnPointerDown()
{
// enable pressed
isPressed= true;
}
// Handle pointer up
public void OnPointerUp()
{
// disable pressed
isPressed= false;
}
// Handle pointer exit
public void OnPointerExit()
{
// disable pressed
isPressed= false;
}
}
Place this component next to a Button component. You don't have to reference anything in onClick just leave it empty. Instead reference something in onPressed. Keep the Button component though since it handles also the UI style for us (like hover changes the color/sprite etc.)
Again: The Update solutions might look cleaner/simplier for now but are not as efficient (in this usecase) and easy to controll (this might be opinion based) as the Coroutine solutions.
Please search on the article regarding the key press functionalities . It would help you a lot in finding your answer. Update is used if we need to do something continuously in our project where as the key pressed is used when we have do it for once
this example is also being used to resolve your issue and use this Script when a specific button is is being pressed

EasyTouch about unity3d Error

I am using Unity3D to create a demo.
The demo is like this: Use the EasyTouch to control the movement of the gameobject named "Player" —— the plane.
After creating the EasyTouch, I create a C# script like this:
using UnityEngine;
using System.Collections;
public class Player : MonoBehaviour {
public float m_speed = 1;
protected Transform m_transform;
public MovePlayer m_movePlayer;
// Use this for initialization
void Start () {
m_transform = this.transform;
}
// Update is called once per frame
void Update () {
}
void OnEnable(){
Debug.Log ("OnEnable");
EasyJoystick.On_JoystickMoveStart += HandleOn_JoystickMoveStart;
EasyJoystick.On_JoystickMove += HandleOn_JoystickMove;
EasyJoystick.On_JoystickMoveEnd += HandleOn_JoystickMoveEnd;
}
void HandleOn_JoystickMoveStart (MovingJoystick move)
{
}
void HandleOn_JoystickMoveEnd (MovingJoystick move)
{
}
void HandleOn_JoystickMove (MovingJoystick move)
{
Debug.Log ("HandleOn_JoystickMove");
if (m_transform == null) {
m_transform = this.transform;
}
if (move.joystickName != "moveJoystick") {
Debug.Log ("return");
return;
}
float currentPositionX = this.gameObject.GetComponent<Transform> ().position.x;
float currentPositionZ = this.gameObject.GetComponent<Transform> ().position.z;
float joyPositionX = move.joystickAxis.x;
float joyPositionY = move.joystickAxis.y;
Debug.Log ("joyPositionX = " + joyPositionX + " joyPositionY = " + joyPositionY);
float moveh = joyPositionX / 10;
float movev = joyPositionY / 10;
this.m_transform.Translate (new Vector3 (-joyPositionX/10, 0, -joyPositionY/10));
}
}
When I start the game, the code worked fine, and the plane will be control.
Then I create the button by GUI.
When the button was clicked, the code Application.LoadLevel(0) will be called.And the level will restart.
But after I called Application.LoadLevel(0) to restart the game.
The plane will can not be control by the EasyTouch, and the Error message will appear:
MissingReferenceException: The object of type 'Player' 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.
I wonder at why the "Player" will be null.
The "Player" should be init after called Application.LoadLevel(0), doesn't it?
Preamble
I have never used this EasyTouch plugin (fortunately? :D). So I'll make an assumptions on what I see in your code. If I'm wrong — sorry :)
Answer
Take a look on this code:
void OnEnable(){
Debug.Log ("OnEnable");
EasyJoystick.On_JoystickMoveStart += HandleOn_JoystickMoveStart;
EasyJoystick.On_JoystickMove += HandleOn_JoystickMove;
EasyJoystick.On_JoystickMoveEnd += HandleOn_JoystickMoveEnd;
}
It looks like EasyJoystick is a static class. And you are subscribing to static events here. Then after calling Application.LoadLevel() the instance of your Player class marked as destroyed. But subscriptions don't go anywhere and once EasyJoystick fires one of the events (On_JoystickMoveStart, On_JoystickMove or On_JoystickMoveEnd) corresponding methods (HandleOn_JoystickMoveStart, HandleOn_JoystickMove or HandleOn_JoystickMoveEnd) are being invoked. This is why you get the exception. And it is probably thrown by this line (am I wrong? :)):
float currentPositionX = this.gameObject.GetComponent<Transform> ().position.x;
In order to fix the problem you need to unsubscribe Player instance(s) from the events mentioned before invoking Application.LoadLevel().
Side note
This:
float currentPositionX = this.gameObject.GetComponent<Transform> ().position.x;
can be rewritten as:
float currentPositionX = transform.position.x;
Though currentPositionX and currentPositionY are not used at all :D
Edit. How to unsubscribe
Something like this:
void OnDisable(){
Debug.Log ("OnDisable");
EasyJoystick.On_JoystickMoveStart –= HandleOn_JoystickMoveStart;
EasyJoystick.On_JoystickMove –= HandleOn_JoystickMove;
EasyJoystick.On_JoystickMoveEnd –= HandleOn_JoystickMoveEnd;
}

Categories

Resources