Moving comparisons out from the Update method: using delegates instead or another approach? - performance

Let's go straight to an example. Let's say we have:
Update(){
if (value.Equals("circular")) moveGameObjectInACircularWay();
else if (value.Equals("linear")) moveGameObjectInALinearWay();
}
I think that is not very elegant solution. Unity needs to perform a comparison every frame. That does not sound very optimal to me. I'm just guessing it should be some other way to implement the same like:
Start () {
if (value.Equals("circular")) movement += moveGameObjectInACircularWay;
else if (value.Equals("linear")) movement += moveGameObjectInALinearWay;
}
Update () {
movement();
}
I guess the solution is related with delegates. That's why my proposed solution looks like delegates. I don't understand what delegates are well yet.

From MSDN "A delegate in C# is similar to a function pointer in C or C++. Using a delegate allows the programmer to encapsulate a reference to a method inside a delegate object." (https://msdn.microsoft.com/en-us/library/aa288459(v=vs.71).aspx) In short is a pointer to a method. What you want to do is the following:
using UnityEngine;
using System.Collections;
public delegate void MovementDelegate();
public class Movement : MonoBehaviour {
MovementDelegate movementFunction=null;
public string value = "linear";
void Start () {
if (value.Equals("circular")) movementFunction = moveGameObjectInACircularWay;
else if (value.Equals("linear")) movementFunction = moveGameObjectInALinearWay;
}
// Update is called once per frame
void Update()
{
if (movementFunction != null)
{
movementFunction();
}
}
void moveGameObjectInACircularWay()
{
Debug.Log("do circular movement here");
}
void moveGameObjectInALinearWay()
{
Debug.Log("do linear movement here");
}
}
The functions you declare must have the same signature as the delegate signature. If you want to add parameters to it, ex. an int, decalre your delegate as
public delegate void MovementDelegate(int speed);
and your implementation functions as
void moveGameObjectInACircularWay(int speed)
void moveGameObjectInALinearWay(int speed)
and change the call to
movementFunction(yourIntHere)
UPDATED!: Thanks to Joe Blow suggestion here is another solution:
public class Movement : MonoBehaviour
{
Action<int> movementFunction = null;
public string value = "linear";
void Start()
{
if (value.Equals("circular")) movementFunction = moveGameObjectInACircularWay;
else if (value.Equals("linear")) movementFunction = moveGameObjectInALinearWay;
}
// Update is called once per frame
void Update()
{
if (movementFunction != null)
{
movementFunction(2);
}
}
void moveGameObjectInACircularWay(int speed)
{
Debug.Log("do circular movement here "+ speed);
}
void moveGameObjectInALinearWay(int speed)
{
Debug.Log("do linear movement here " + speed);
}
}

My favorite answer has been written by Joe Blow in the comments:
Unity is components based. We better switch to Component-Based Thinking instead working with delegates.
So make two (or more) different scripts, and put those on the game object in question. Then, turn on and off these components as you wish.
So we would have to scripts added to our game object: MoveGameObjectInACircularWay.cs and MoveGameObjectInALinearWay.cs. Then a MainGameObjectScript.cs also added to our game object with the following code:
void Start () {
GetComponent()<MoveGameObjectInACircularWay>.active = true;
GetComponent()<MoveGameObjectInALinearWay>.active = false;
}

Related

How to move a cube 3 second in one direction, and next 3 seconds in oppposite direction, alternatively

I am new to unity, and trying something like below, but I can either move only in one direction, or not moving at all.
My cube is a trigger, and is not using gravity. I have checked the Kitematic box. I am trying to make the cube move to and fro, so that player have difficuly collecting it.
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
public class movedanger : MonoBehaviour
{
private int mytime = 0;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
MyMover(mytime);
}
void MyMover(int mytime)
{
if (mytime <= 3)
{
transform.Translate(Vector3.forward * Time.deltaTime);
mytime++;
}
else
{
transform.Translate(-Vector3.forward * Time.deltaTime);
mytime = 1;
}
}
}
What you are looking for is to and fro movement of an object. You can achieve this with Mathf.PingPong() function instead of using translate. I have tested it with a cube, you can set the minimum and maximum distance it should move to and the speed at which it travels. Since you want the cube to move 3 seconds in one direction at a time. You can calculate the speed as distance/time so the max distance it should travel to from the current distance and the time (3 seconds) it takes. Hope this helps.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveCube : MonoBehaviour {
public float min = 2f;
public float max = 8f;
public float SpeedOfMovement = 2f;
// Start is called before the first frame update
void Start () {
}
// Update is called once per frame
void Update () {
transform.position = new Vector3 (Mathf.PingPong (Time.time * SpeedOfMovement, max - min) + min, transform.position.y, transform.position.z);
}
}
With InvokeRepeating you will call the same MoveCube method every 3 seconds.
using UnityEngine;
public class MoveDanger: MonoBehaviour
{
public bool isForward = false;
private void Start()
{
InvokeRepeating("MoveCube", 0f, 3f);
}
private void MoveCube()
{
if (isForward)
{
transform.Translate(Vector3.back);
isForward = false;
}
else
{
transform.Translate(Vector3.forward);
isForward = true;
}
}
}
Honestly the best and easiest way to do something like this, once you get used to it, is just to
Use Unity's incredibly simple animator system:
(Essentially just click "new animation" and then drag the object around as you want it animated.)
There are 100s of tutorials online explaining how to use it.
It's one of those things where once you use it and see how easy it is, you will do a "facepalm" and never bother again with other ways.
It's really "the Unity way" to achieve the goal here, dead easy and flexible.

Getting errors when i try to get game to restart when i hit an obstacle or fall of ground

Please excuse me I'm a complete novice at all this but I'm trying to make a game following "Brackeys How To Make A Video Game" I'm on video 8 if that helps. I can't seem to find what i have done wrong i have added my scripts for "player movement", "player collision" and "game manager". Please if there is anything else you need to help me please ask i really don't want to give up just yet was really enjoying doing this.
Thank you all
using UnityEngine;
public class PlayerMovement : MonoBehaviour {
// This is a reference to the Rigidbody component called "rb"
public Rigidbody rb;
public float forwardForce = 2000f; // Variable that determines the forward force
public float sidewaysForce = 500f; // Variable that determines the sideways force
// We marked this as "Fixed"Update because we
// are using it to mess with physics.
void FixedUpdate ()
{
// Add a forward force
rb.AddForce(0, 0, forwardForce * Time.deltaTime);
if (Input.GetKey("d")) // If the player is pressing the "d" key
{
// Add a force to the right
rb.AddForce(sidewaysForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
}
if (Input.GetKey("a")) // If the player is pressing the "a" key
{
// Add a force to the left
rb.AddForce(-sidewaysForce * Time.deltaTime, 0, 0, ForceMode.VelocityChange);
}
if (rb.position.y < -1f)
{
FindObjectOfType<GameManager>().EndGame();
}
}
}
using UnityEngine;
public class PlayerCollision : MonoBehaviour {
public PlayerMovement movement; // A reference to our PlayerMovement script
// This function runs when we hit another object.
// We get information about the collision and call it "collisionInfo".
void OnCollisionEnter (Collision collisionInfo)
{
// We check if the object we collided with has a tag called "Obstacle".
if (collisionInfo.collider.tag == "Obstacle")
{
movement.enabled = false; // Disable the players movement.
FindObjectOfType<GameManager>().EndGame();
}
}
}
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour {
bool gameHasEnded = false;
public float restartDelay = 1f;
public GameObject completeLevelUI;
public void CompleteLevel ()
{
completeLevelUI.SetActive(true);
}
public void EndGame ()
{
if (gameHasEnded == false)
{
gameHasEnded = true;
Debug.Log("GAME OVER");
Invoke("Restart", restartDelay);
}
}
void Restart ()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
when i fall off ground:
NullReferenceException: Object reference not set to an instance of an object
PlayerMovement.FixedUpdate () (at Assets/Scripts/PlayerMovement.cs:32)
when i hit an obstacle:
NullReferenceException: Object reference not set to an instance of an object
PlayerCollision.OnCollisionEnter (UnityEngine.Collision collisionInfo) (at Assets/Scripts/PlayerCollision.cs:15)
It seems to me that FindObjectOfType<GameManager>() is returning null, which is causing a null reference exception when you attempt to call the EndGame function. This most likely means that there is no object in your scene with the GameManager component on it. The solution to this problem is simple:
Create an empty object in your scene and add the GameManager component to it. This will fix the error in this instance, but it could happen in the future if you're not careful. It is also a good idea to check if you found an object or not before calling functions on it:
GameManager gm = FindObjectOfType<GameManager>();
if (gm != null)
{
gm.EndGame();
}

How would I change the colour of an object using 'OnMouseEnter'?

The script used to be:
function OnMouseEnter()
{
renderer.material.color = Color.grey;
}
But using that is now obsolete after an update and I have no idea what the current syntax is or how one would go about finding it out. I've searched everywhere and couldn't find an answer.
Since Unity 4.6 there is a new way of handling input events. One have to use interfaces from UnityEngine.EventSystems namespace. Look at this example:
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems; // dont forget this
public class SomeController : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler
{
private bool hovered = false;
// from IPointerEnterHandler
public void OnPointerEnter(PointerEventData eventData)
{
hovered = true;
}
// from IPointerExitHandler
public void OnPointerExit(PointerEventData eventData)
{
hovered = false;
}
// from IPointerClickHandler
public void OnPointerClick(PointerEventData eventData)
{
// send some event
}
}
Still, you have to add collider component to your object.

Can mousePressed be defined within a class?

I am trying to create a class called InputManager to handle mouse events. This requires that mousePressed be contained within the InputManager class.
like so
class InputManager{
void mousePressed(){
print(hit);
}
}
problem is, that doesn't work. mousePressed() only seems to work when it's outside the class.
How can I get these functions nicely contained in a class?
Try this:
in main sketch:
InputManager im;
void setup() {
im = new InputManager(this);
registerMethod("mouseEvent", im);
}
in InputManager Class:
class InputManager {
void mousePressed(MouseEvent e) {
// mousepressed handling code here...
}
void mouseEvent(MouseEvent e) {
switch(e.getAction()) {
case (MouseEvent.PRESS) :
mousePressed();
break;
case (MouseEvent.CLICK) :
mouseClicked();
break;
// other mouse events cases here...
}
}
}
Once you registered InputManger mouseEvent in PApplet you don't need to call it and it will be called each loop at the end of draw().
Most certainly, but you are responsible for ensuring it gets called:
interface P5EventClass {
void mousePressed();
void mouseMoved();
// ...
}
class InputManager implements P5EventClass {
// we MUST implement mousePressed, and any other interface method
void mousePressed() {
// do things here
}
}
// we're going to hand off all events to things in this list
ArrayList<P5EventClass> eventlisteners = new ArrayList<P5EventClass>();
void setup() {
// bind at least one input manager, but maybe more later on.
eventlisteners.add(new InputManager());
}
void draw() {
// ...
}
void mousePressed() {
// instead of handling input globally, we let
// the event handling obejct(s) take care of it
for(P5EventClass p5ec: eventlisteners) {
p5ec.mousePressed();
}
}
I would personally make it a bit tighter by also passing the event variables explicitly, so "void mousePressed(int x, int y);" in the interface and then calling "p5ec.mousePressed(mouseX, mouseY);" in the sketch body, simply because relying on globals rather than local variables makes your code prone to concurrency bugs.
The easiest way to do so would be:
class InputManager{
void mousePressed(){
print(hit);
}
}
InputManager im = new InputManager();
void setup() {
// ...
}
void draw() {
// ...
}
void mousePressed() {
im.mousePressed();
}
This should solve any problems you were having with variable scoping in your class.
Note: In the class, it doesn't even have to be named mousePressed, you can name it anything you wish, as long as you call it inside of the main mousePressed method.

C# like events in D programming language

I recently finished a 6-month internship at a company that uses C# for the most part of their programming. During this time I first used and got accustomed to the C# way of doing events. Like shown below:
acc.AccountBalanceLow += new AccountBalanceDelegate(atm.AccountToLow);
acc.AccountBalanceLow +=new AccountBalanceDelegate(atm.AccountToLowAgain);
Does D support such constructs? I'd imagine one could be created by the user by using operator overloading, but I'm not entirely sure. If it's not possible what would then be a common excepted way of doing it then?
The equivalent construct in D is to use Signals and Slots. This is a different means of implementing the Observer Pattern, which is effectively what a C# event does.
D (and C++) use an analogous pattern called signals and slots.
If you're feeling the need to use the C# style-events instead of signals and slots, they're extremely simple to implement:
module fluidity.core.event;
class Event {
alias void delegate(EventArgs) handler_t;
handler_t[] handlers;
Object owner;
this() {}
this(Object o) { owner = o; }
void attach(handler_t handler) {
if (handler)
handlers ~= handler;
}
void detach(handler_t handler) {
int i = -1;
foreach (j, h; handlers)
{
if (h is handler)
{
i = j;
break;
}
}
if (i > -1)
handlers = handlers[0..i] ~ handlers[i+1..$];
}
void raise() { raise(new EventArgs(owner)); }
void raise(EventArgs e) {
// call all handlers
foreach (handler; handlers)
{
if (handler)
handler(e);
}
}
void opAddAssign(handler_t handler) {
attach(handler);
}
void opSubAssign(handler_t handler) {
detach(handler);
}
}
class EventArgs {
Object source;
bool handled;
void handle() { handled = true; }
this() {}
this(Object s) {
source = s;
}
}
Here is an example of c# style events using signals, slots, and a templates:
events.d:
import std.signals;
class Event(T...){
mixin Signal!(T);
void broadcast(T args){
emit(args);
}
void opAddAssign(slot_t slot){
connect(slot);
}
void opSubAssign(slot_t slot) {
disconnect(slot);
}
}
declaration:
public Event!(int) onSomeEventOfInt;
public Event!(string, int) onSomeEventOfStringAndInt;
instantiation:
this.onSomeEventOfInt = new Event!(int)();
this.onSomeEventOfStringAndInt = new Event!(string, int)();
fire event:
int i = 4;
string str = "hello";
this.onSomeEventOfInt.broadcast(i);
this.onSomeEventOfStringAndInt.broadcast(str, 4);
observer registration:
obj1.onSomeEventOfInt += &handleEventOfInt
obj1.onSomeEventOfStringAndInt += &handleEventOfStringAndInt
void handleEventOfInt(int g)
{ /*do something */ }
void handleEventOfStringAndInt(string str, int g)
{ /*do something */ }
Check out DFL's event system. It works EXACTLY the same way as C# .NET.
DFL Event Example
Download DFL, grab the events module and use it the way you like. I modified it to use variadic template arguments. This gives maximum flexibility.
http://www.dprogramming.com/dfl098.zip

Resources