I was just getting started with Unity and using it's Standard Assets library. I used its rollerball object, but both its Ball and BallUserControl scripts gave me the following error:
No Monobehaviour scripts in the file, or the names do not match the file name.
I didn't change any scripts, and I have no other objects or scripts in my environment. The class names match the file names. I am sure I am missing something obvious (being so new). What are some possible errors?
I am running the latest version of unity on windows 10.
Here's the BallUserControl script, as requested:
using System;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;
namespace UnityStandardAssets.Vehicles.Ball
{
public class BallUserControl : MonoBehaviour
{
private Ball ball; // Reference to the ball controller.
private Vector3 move;
// the world-relative desired move direction, calculated from the camForward and user input.
private Transform cam; // A reference to the main camera in the scenes transform
private Vector3 camForward; // The current forward direction of the camera
private bool jump; // whether the jump button is currently pressed
private void Awake()
{
// Set up the reference.
ball = GetComponent<Ball>();
// get the transform of the main camera
if (Camera.main != null)
{
cam = Camera.main.transform;
}
else
{
Debug.LogWarning(
"Warning: no main camera found. Ball needs a Camera tagged \"MainCamera\", for camera-relative controls.");
// we use world-relative controls in this case, which may not be what the user wants, but hey, we warned them!
}
}
private void Update()
{
// Get the axis and jump input.
float h = CrossPlatformInputManager.GetAxis("Horizontal");
float v = CrossPlatformInputManager.GetAxis("Vertical");
jump = CrossPlatformInputManager.GetButton("Jump");
// calculate move direction
if (cam != null)
{
// calculate camera relative direction to move:
camForward = Vector3.Scale(cam.forward, new Vector3(1, 0, 1)).normalized;
move = (v*camForward + h*cam.right).normalized;
}
else
{
// we use world-relative directions in the case of no main camera
move = (v*Vector3.forward + h*Vector3.right).normalized;
}
}
private void FixedUpdate()
{
// Call the Move function of the ball controller
ball.Move(move, jump);
jump = false;
}
}
}
If monobeviour files are missing or not showing in the visual studio and creates error, Go to Edit/Preferences/External Tools/ select visual studio version and
Regenerate project files. It worked for me.
Related
Hi!
After the discussion with Ruzihm in the comments. I've now created a simple version of my game to better ask the question I'm having.
The question now is, since I'm not able to manually create a connection to the testObject field in the inspector. How do I now tell Unity to use my instantiated objects while the game is running?
And is this a good solution for a RTS game that may have 100s of Units active at a time? The end goal here is to apply this force to a radius around the cursor. Which I was thinking of using Physics.OverlapSphere
Here's the minimal scenario of what I have:
New Unity scene
Attached the InputManager to the main camera.
Created a capsule and a plane.
Added ApplyForce to the Capsule
Created a prefab from the capsule and deleted it from the scene.
In the InputManager I added the ability to press space to Instantiate a capsule with the ApplyForce script attached..
Drag the capsule prefab to the InputManager "objectToGenerate"
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace GL.RTS.Mites
{
public class InputManager : MonoBehaviour
{
public GameObject testObject;
public ApplyForce onSpawnTest;
public GameObject objectToGenerate;
void Start()
{
onSpawnTest = testObject.GetComponent<ApplyForce>();
}
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
Instantiate(objectToGenerate);
}
if (Input.GetMouseButton(0))
{
onSpawnTest.PushForward();
}
}
}
}
The ApplyForce script that I attach to the Capsule:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace GL.RTS.Mites
{
public class ApplyForce : MonoBehaviour
{
public float moveSpeed;
Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
Debug.Log("A Mite has spawned!");
}
public void PushForward()
{
rb.AddRelativeForce(Vector3.up * moveSpeed * Time.deltaTime);
Debug.Log("A force of: " + moveSpeed + " is being added.");
}
}
}
Well, you are creating your new instances of your object, but your input manager immediately forgets about them (note that you do nothing with the return value). The InputManager only knows about the ApplyForce that was created in its Start (and then interacts with it depending on mouse input) and your ApplyForce script knows nothing about any InputManager. So, it should come as no surprise that only the first instance reacts to the mouse input.
So, something has to be done to your InputManager and/or your ApplyForce. Your InputManager could remember the instances it creates (which isn't enough, because what if for example, a map trigger creates new player controllable units) or it could go looking for units each time.
Your ApplyForce could register with the InputManager when they are created, but then you would need to loop through the units and find out which ones are under the mouse, anyway.
Since you only want to select ones based on what is near or under your cursor and only when input occurs and not like every frame, I would go with the simplest approach, just letting your InputManager find the units when it needs them. Something like below, explanation in comments:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace GL.RTS.Mites
{
public class InputManager : MonoBehaviour
{
public GameObject testObject;
public ApplyForce onSpawnTest;
public GameObject objectToGenerate;
private Camera mainCam;
// which layers to consider for cursor detection
[SerializeField] LayerMask cursorLayerMask;
// how big for cursor detection
[SerializeField] float cursorRadius;
void Awake()
{
// cache main camera
mainCam = Camera.main;
}
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
Instantiate(objectToGenerate);
}
if (Input.GetMouseButton(0))
{
Collider[] colls = FindCollidersUnderCursor();
// check each collider for an applyforce and use it if present
foreach( Collider coll in colls)
{
ApplyForce af = coll.GetComponent<ApplyForce>();
if (af != null)
{
af.PushForward();
}
}
}
}
Collider[] FindCollidersUnderCursor()
{
// find ray represented by cursor position on screen
// and find where it intersects with ground
// This technique is great for if your camera can change
// angle or distance from the playing field.
// It uses mathematical rays and plane, no physics
// calculations needed for this step. Very performant.
Ray cursorRay = mainCam.ScreenPointToRay(Input.mousePosition);
Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
if (groundPlane.Raycast(cursorRay, out float cursorDist))
{
Vector3 worldPos = cursorRay.GetPoint(cursorDist);
// Check for triggers inside sphere that match layer mask
return Physics.OverlapSphere(worldPos, cursorRadius,
cursorLayerMask.value, QueryTriggerInteraction.Collide);
}
// if doesn't intersect with ground, return nothing
return new Collider[0];
}
}
}
Of course, this will require that every unit you're interested in manipulating has a trigger collider.
I am trying to complete a project using unity3D standard assets. But while importing the assets the scripts were creating an error - "No MonoBehavior scripts in the file, or their names do not match the file name." and another error - "Assets\Standard Assets\Characters\RollerBall\Scripts\BallUserControl.cs(3,27): error CS0234: The type or namespace name 'CrossPlatformInput' does not exist in the namespace 'UnityStandardAssets' (are you missing an assembly reference?)". As I am new to unity and although I have prior knowledge but not in C# I am not understanding the errors. I have also given a script below for finding the problem.
using System;
using UnityEngine;
namespace UnityStandardAssets.Vehicles.Ball
{
public class Ball : MonoBehaviour
{
[SerializeField] private float m_MovePower = 5; // The force added to the ball to move it.
[SerializeField] private bool m_UseTorque = true; // Whether or not to use torque to move the ball.
[SerializeField] private float m_MaxAngularVelocity = 25; // The maximum velocity the ball can rotate at.
[SerializeField] private float m_JumpPower = 2; // The force added to the ball when it jumps.
private const float k_GroundRayLength = 1f; // The length of the ray to check if the ball is grounded.
private Rigidbody m_Rigidbody;
private void Start()
{
m_Rigidbody = GetComponent<Rigidbody>();
// Set the maximum angular velocity.
GetComponent<Rigidbody>().maxAngularVelocity = m_MaxAngularVelocity;
}
public void Move(Vector3 moveDirection, bool jump)
{
// If using torque to rotate the ball...
if (m_UseTorque)
{
// ... add torque around the axis defined by the move direction.
m_Rigidbody.AddTorque(new Vector3(moveDirection.z, 0, -moveDirection.x)*m_MovePower);
}
else
{
// Otherwise add force in the move direction.
m_Rigidbody.AddForce(moveDirection*m_MovePower);
}
// If on the ground and jump is pressed...
if (Physics.Raycast(transform.position, -Vector3.up, k_GroundRayLength) && jump)
{
// ... add force in upwards.
m_Rigidbody.AddForce(Vector3.up*m_JumpPower, ForceMode.Impulse);
}
}
}
}
I want to add doors to my Unity project and so far I have gotten the code to work with my door but I can open it from any distance in the scene. I want to be able to only open it from a small distance away but I cannot figure out how.
public class Door : MonoBehaviour
{
public bool doorIsOpen;
public Transform closedPos;
public Transform openPos;
public float openSpeed;
public float openRadius;
void Update()
{
if(Physics.CheckSphere(gameObject.transform.position, openRadius))
{
if(Input.GetKeyDown(KeyCode.E))
{
doorIsOpen = !doorIsOpen;
}
}
if(doorIsOpen == true)
{
OpenDoor();
}
if(doorIsOpen == false)
{
CloseDoor();
}
}
void OpenDoor()
{
doorIsOpen = true;
gameObject.transform.position = openPos.position;
gameObject.GetComponent<Animator>().SetTrigger("OpenDoor");
gameObject.GetComponent<Animator>().SetBool("DoorIsClosed", false);
gameObject.GetComponent<Animator>().SetBool("DoorIsOpen", true);
}
void CloseDoor()
{
doorIsOpen = false;
gameObject.transform.position = closedPos.position;
gameObject.GetComponent<Animator>().SetTrigger("CloseDoor");
gameObject.GetComponent<Animator>().SetBool("DoorIsOpen", false);
gameObject.GetComponent<Animator>().SetBool("DoorIsClosed", true);
}
}
Prerequisites
Add a sphere with a collision body around the door instance in Unity. This sphere functions as the radius which will trigger the ChangeDoorState method.
Change the update method
The update will look at any collisions happening in the specified sphere. If there is at least one object in range (the collision sphere) of the door, then it opens or closes the door instance. Source: https://docs.unity3d.com/ScriptReference/Physics.OverlapSphere.html
void Update()
{
Collider[] hitColliders = Physics.OverlapSphere(center, radius);
if (hitColliders.Length > 0)
{
ChangeDoorState();
}
}
Merged the OpenDoor and CloseDoor methods
void ChangeDoorState()
{
doorClosureState = doorClosure ? true : false;
gameObject.transform.position = doorClosureState ? closedPos.position : openPos.postition;
gameObject.GetComponent<Animator>().SetTrigger("DoorClosure");
gameObject.GetComponent<Animator>().SetBool("DoorIsOpen", doorClosureState);
gameObject.GetComponent<Animator>().SetBool("DoorIsClosed", !doorClosureState);
}
You can increase the value of 'openRadius', the game is creating a sphere at gameObject.transform.position with a radius of 'openRadius' and checking if there is any colliders overlapping the sphere.
//...
if(Physics.CheckSphere(gameObject.transform.position, openRadius))
//...
One issue is that you permanently set the triggers in every frame. You would only want to do so when you hit the key.
Then also Physics.CheckSphere checks whether there is any collider within the range. This could be any collider, not only the player. To make sure it only works if the player is the one in range you should definitely use Layers and give the player its own layer e.g. "Player". Then you can pass the layerMask parameter to make sure the doors only check for the player's layer.
so I would simply use
// if possible already reference this in the Inspector
[SerializeField] private Animator _animator;
[Tooltip("Here select only the Layer(s) which you assigned to your Player object(s)")]
[SerializeField] LayerMask _playerLayer;
private void Awake()
{
// as fallback get it ONCE on runtime
if(!_animator) _animator = GetComponent<Animator>();
}
private void Update()
{
// Afaik the Key is way cheaper to check so do it first
// and use physics only if actually necessary
if(Input.GetKeyDown(KeyCode.E))
{
// Only check for the Player's Layer and ignore other colliders
if(Physics.CheckSphere(transform.position, openRadius, _playerLayer.value))
{
ToggleDoorState();
}
}
}
// Since manually changing the flag in the Inspector will not have
// any effect anymore in order to be able to test it you can use
// the context menu of this component in the Inspector
[ContextMenu(nameof(ToggleDoorState))]
private void ToggleDoorState()
{
// invert the flag
doorIsOpen = !doorIsOpen;
// use the flag in ternary expressions
transform.position = doorIsOpen ? openPos.position : closedPos.position;
_animator.SetTrigger(doorIsOpen ? "OpenDoor" : "CloseDoor");
// use the value of the flag diectly
_animator.SetBool("DoorIsClosed", !doorIsOpen);
_animator.SetBool("DoorIsOpen", doorIsOpen);
}
It is seems a bit though as if you have a bit of redundancy in your animator. I would either use the Bools in order to trigger a transition or use the Triggers, to have both seems odd.
I am very new to unity and am building a VR app for Oculus Go. I want to pick and move the object by pointing the ray from the controller on the object and then picking or releasing it by pressing the trigger button. I want the object to stay fixed at the end of the ray's position rather than coming suddenly onto the controller. I have used this script to create a ray and basically allow the controller to pick it up but this script shits the object to the controller's position and as a result I can only move object in a circle(in 360 degrees). It also does not drop the object correctly, as the objects continue to float.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerPointer : MonoBehaviour {
//Returns whatever object is infrount of the controller
private GameObject pointerOver;
[SerializeField]
//Is the object that is currently intractable
private PropBase selectedObject;
//Is the object currently stored in hand, ready to throw.
[SerializeField]
private PickUp inHand;
//This is a refrance to the object we want the pointer to be cast from.
[SerializeField]
public Transform controllerRef;
//This is where we want object we are holding to appear
[SerializeField]
private Transform holdingRef;
//The amount of force we want to throw objects from our hand with.
[SerializeField]
[Range(2,12)]
private float throwForce = 10;
//The script that handles the visuals to show what object is selected
[SerializeField]
private HighlightObject selectVisual;
private LineRenderer line;
void Start () {
line = GetComponent<LineRenderer> ();
}
void Update () {
//If a object is currently being held I don't want to select another
object until it is thrown.
if (inHand == null) {
WorldPointer ();
} else {
line.SetPosition (0, controllerRef.position);
line.SetPosition (1, controllerRef.position);
pointerOver = null;
}
//This function handles how you intract with selected objects
Intract ();
}
//This function handles shooting a raycast into the world from the
controller to see what can be intracted with.
void WorldPointer(){
//We set the line visual to start from the controller.
line.SetPosition (0, controllerRef.position);
RaycastHit hit;
//We reset the pointer so things don't stay selected when we are
pointing at nothing.
pointerOver = null;
//This sends a line from the controller directly ahead of it, it returns
true if it hits something. Using the RaycastHit we can then get information
back.
if (Physics.Raycast (controllerRef.position, controllerRef.forward, out
hit)) {
//Beacuse raycast is true only when it hits anything, we don't need
to check if hit is null
//We set pointerOver to whatever object the raycast hit.
pointerOver = hit.collider.gameObject;
//We set the line visual to stop and the point the raycast hit the
object.
line.SetPosition (1, hit.point);
//Here we check if the object we hit has the PropBase component, or
a child class of its.
if (pointerOver.GetComponent<PropBase> ()) {
//We set the object to be highlighted
selectVisual.NewObject (pointerOver);
} else {
selectVisual.ClearObject ();
}
} else {
//If the raycast hits nothing we set the line visual to stop a
little bit infrount of the controller.
line.SetPosition (1, controllerRef.position + controllerRef.forward
* 10);
selectVisual.ClearObject ();
}
Debug.DrawRay(controllerRef.position , controllerRef.forward *
10,Color.grey);
}
void Intract(){
//We set up the input "OculusTouchpad" in the Input manager
if (Input.GetButtonDown ("Jump") || OVRInput.GetDown
(OVRInput.Button.PrimaryTouchpad)) {
selectVisual.ClearObject ();
//Check if you are holding something you can throw first
if (inHand != null) {
inHand.Release (controllerRef.forward, throwForce);
inHand = null;
//We do this check here to prevent Errors if you have nothing
selected
} else if (selectedObject != null) {
//Check if you can pick up the selected object second
if (selectedObject.GetComponent<PickUp> ()) {
//Beacuse PickUp is a child of PropBase, we can ask InHand
to store selectedObject as PickUp, rather than use GetComponent
inHand = selectedObject as PickUp;
inHand.Store (holdingRef);
//If non of the above were valid then simple call the
trigger function of the selected object
} else {
selectedObject.Trigger ();
}
}
//If you have a object that you need to hold down a button to
intract with
} else if (Input.GetButton ("Jump") && selectedObject != null ||
OVRInput.Get (OVRInput.Button.PrimaryTouchpad) && selectedObject != null) {
selectedObject.Pulse ();
//When you are not pressing down the touchpad button, the selected
object can be updated
} else if (pointerOver != null) {
if (pointerOver.GetComponent<PropBase> ()) {
selectedObject = pointerOver.GetComponent<PropBase> ();
} else {
selectedObject = null;
}
} else {
selectedObject = null;
}
}
}
And i have attached this script to the objects I want to pick:
public class PickUp : PropBase
{
private Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
}
public virtual void Store(Transform NewParent)
{
//The following stops the object being effected by physics while it's in
the players hand
rb.isKinematic = true;
//And fixes it to the new parent it is given by the player script to
follow.
transform.parent = NewParent;
//It then resets it's position and rotation to match it's new parent
object
transform.localRotation = Quaternion.identity;
transform.localPosition = Vector3.zero;
}
public virtual void Release(Vector3 ThrowDir, float ThrowForce)
{
//On Release the object is made to be effected by physics again.
rb.isKinematic = false;
//Free itself from following it's parent object
transform.parent = null;
//And applies a burst of force for one frame to propel itself away from
the player.
rb.AddForce(ThrowDir * ThrowForce, ForceMode.Impulse);
}
}
What i'd like to see is have the position of the sphere change according to wherever the end of the ray is cast.
I have also attached this script to the player contoller, which allows it to move to a point by pointing to it and pressing the touchpad button.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ClickToMove : MonoBehaviour
{
private Vector3 targetPos; //This Vector3 will store the position where we
click to move.
private bool Moving = false; /*This bool keeps track of whether we are in
the process of moving or not.*/
private GameObject targetInstance;
/*The variables we want to customize. Added info headers to these for the
Unity Editor.*/
[Header("Our Go controller object")]
public GameObject goController;
[Header("Movement Speed")]
public float speed = 1;
[Header("Stop When This Far Away From Target")]
public float haltDistance = 0;
[Header("Optional Target Object")]
public GameObject targetObj;
void Update()
{
MoveToTarget(); /*Here we simply run our MoveToTarget method in the
Update method.*/
//That way we don't clutter up the Update method with too much code.
}
void MoveToTarget() //Here we do the cluttering instead.
{
var ray = new Ray(goController.transform.position,
goController.transform.forward); /*Create a ray going from the goController
position and in the Forward direction of the goController.*/
RaycastHit hitInfo; //Store info about what the ray hits.
Physics.Raycast(ray, out hitInfo, 100);
if (OVRInput.GetUp(OVRInput.Button.PrimaryTouchpad)) /*If we release the
trigger..*/
{
targetPos = hitInfo.point; /*Make our targetPos assume the
positional value of the hit point.*/
if (targetObj) /*If we have specified a Target Object to mark where
we click*/
//If we didn't, then we don't want to try to instantiate it.
{
if (targetInstance) /*If there is already a Target Object in the
scene.*/
{
Destroy(targetInstance); //Destroy it.
}
targetInstance = Instantiate(targetObj, targetPos,
transform.rotation); //Create our Target object at the position we clicked.
}
Moving = true; //And finally we set Moving to True.
}
if (Moving == true) //Since Moving is now true
{
transform.position = Vector3.MoveTowards(transform.position, new
Vector3(targetPos.x, transform.position.y, targetPos.z), speed *
Time.deltaTime); /*Transform our x and z position to move towards the
targetPos.*/
/*Note that our y position is kept at default transform position
since we only want to move along the ground plane.*/
}
if (Vector3.Distance(transform.position, targetPos) <= haltDistance + 1)
/*Check proximity to targetPos. Mainly useful to keep your player from
setting a target position right next to say a building and then end up
clipping through half of it.*/
{
if (targetInstance) //If we created a Target Object..
{
Destroy(targetInstance); //Then we want to destroy it when we
reach it.
}
Moving = false; //Since we have now arrived at our target
//destination.
}
}
}
If anyone could point me in a right direction or help me with this, I would greatly appreciate it!
Thanks in advance.
Okay, with your updated question its now possible to try and answer.
First off - have you tried not resetting your BaseProp localPosition to the controller's?
Try commenting the line that says
transform.localPosition = Vector3.zero;
This wil still orient the object and parent it to the controller but will lock it in a position relative to the moment of parenting.
You currently use "holdingRef" object as a place where the object appears. You may want to use "controllerRef" instead.
To vary distance at which the object appears you can set the object position to:
controllerRef.position+ distance*controllerRef.forward
As this is the direction in which you fire your raycasts. You can get the hit distance by querying hit.distance.
If for any reason that doesn't work out for you, the very point of the raycast hitting the collider is available within HitInfo, so with hit.point you can extract the hit position and position the object relative to that point. Another very useful attribute of hitinfo is .normal, which enables you to get direction at which the hit happened.
You can pass that info along with your Store method.
Using Unity2017.3.1f1 Personal (64 bit) to build a VR app for Android, using Cardboard VR SDK. The purpose of the app is to allow users visualize data in an immersive way.
Currently want to do something similar to the scene view where one can move forward by going where one's looking but with the Cardboard single button i'm just gonna give the ability to move forward.
Built a Canvas where user can select what type of locomotion he/she wants: teleport or walk.
The teleport works fine as you can see here.
When the user selects walk, the following error shows in the Console:
NullReferenceException: Object
reference not set to an instance of an
object
Player.TryWalk () (atAssets/Player.cs:44)
Player.Update () (at Assets/Player.cs:37)
My Player script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum InputMode
{
NONE,
TELEPORT,
WALK
}
public class Player : MonoBehaviour {
public static Player instance; //Singleton design pattern: only one instance of the class appears
public InputMode activeMode = InputMode.NONE;
[SerializeField]
private float playerSpeed = 3.0f;
void Awake()
{
if(instance != null)
{
GameObject.Destroy(instance.gameObject);
}
instance = this;
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update ()
{
TryWalk();
}
public void TryWalk()
{
if(Input.GetMouseButton(0) && activeMode == InputMode.WALK)
{
Vector3 forward = Camera.main.transform.forward;
Vector3 newPosition = transform.position + forward * Time.deltaTime * playerSpeed;
transform.position = newPosition;
}
}
}
The Player script was added as component of Player:
When the button Walk is pressed, the Active Mode changes to WALK, as you can see in the next image.
Still, even though this happens, the user is not able to Walk.
What can I do to solve this?
Camera.main; was returning null.
In order to fix it, had to have the camera in my scene tagged MainCamera as you can see in the next image.
See it working here.