I am trying to write this script that allows me to toggle a transform change on/off on a sphere. I think the logic is right but I really can't figure out what I'm missing here. (I am also getting (line 7)error CS0108: 'Scale.transform' hides inherited member 'Component.transform'. Use the new keyword if hiding was intended.)
By the way I'm a total beginner, this might be very simple but this is my first game ever!
Here's the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Scale : MonoBehaviour
{
public Transform transform;
Vector3 scale;
public float objectSize = 3.0f;
void ScaleOnOff()
{
transform = GetComponent<Transform>();
if (Input.GetKeyDown(KeyCode.S))
{
if (scale.x != objectSize)
{
scale = transform.localScale;
scale.x = objectSize;
transform.localScale = scale;
Debug.Log("condition1");
}
if (scale.x == objectSize)
{
scale = transform.localScale;
scale.x = 1;
transform.localScale = scale;
Debug.Log("condition2");
}
}
}
// Update is called once per frame
void Update()
{
ScaleOnOff();
}
}
Also, in the console both of the Debug.Log messages appear, so I'm wondering how can both conditions be true at the same time?
Thanks for your help!
There are a few errors, but that's normal if you are a beginner :).
There is already a variable called transform. Unity has some
shortcuts for getting components: the most used is transform. If you
try to delete the "Transform transform" line, in which you rightly
declare a variable, you will see that the code will give you no
errors. The variable provided by Unity is the same as GetComponent(), so you can delete this line as well, because
transform is by default the same as that.
In general, if the variables do not change during the game, as in
this case with transform, the values must be taken at the beginning
of the game, or at least a few times, not continuously. In your code
this is not true because the transform variable as mentioned in the
previous point already has a value, but for any other variable you
assign a value that does not change, it is best to do it in Awake()
or Start().
Both messages are shown because the code does the following: if you
pressed the button, it checks if the value of scale.x is different
from "objectSize". (in the first frame scale.x is equal to 0 because
you haven't assigned it yet), If it's true (in the first frame it's
true), among other things it does, it makes scale.x equal to
objectSize. Continuing the code check if scale.x is equal to
"objectSsize" (which in the first frame as explained is true), then
also execute the second debug.log(). To solve this problem you can
try assigning the value of transform.localScale to "scale" in Start(), and instead use two separate if's, an if and an else. On else you
can find a lot of documentation online, so I don't want to explain it
here.
All together something like e.g.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Scale : MonoBehaviour
{
Vector3 scale;
public float objectSize = 3.0f;
void Start()
{
scale = transform.localScale;
}
void ScaleOnOff()
{
if (Input.GetKeyDown(KeyCode.S))
{
if (scale.x != objectSize)
{
scale = transform.localScale;
scale.x = objectSize;
transform.localScale = scale;
Debug.Log("condition1");
}
else
{
scale = transform.localScale;
scale.x = 1;
transform.localScale = scale;
Debug.Log("condition2");
}
}
}
// Update is called once per frame
void Update()
{
ScaleOnOff();
}
}
Excuse me if I was long-winded, but being that you are a beginner, I wanted to explain everything to you well.
Good development!
Related
So I'm new to unity and trying to create a cube programmatically at the start, and when you press the down arrow key it decrements gradually.
using System.Collections.Generic;
using UnityEngine;
public class LetThereBeLight : MonoBehaviour
{
public GameObject rubixCube;
public Vector3 newScale;
public Vector3 currentScale;`
void Start()
{
GameObject rubixCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
rubixCube.transform.localScale = new Vector3(10f, 10f, 10f);
rubixCube.transform.position = new Vector3(0, 0, 0);
currentScale = rubixCube.transform.localScale;
newScale = new Vector3(-1f, -1f, -1f);
}
void Update()
{
if (Input.GetKey(KeyCode.DownArrow)){
currentScale += newScale;
if(currentScale.x < 10){
print("Well at least this worked");
}
}
}
}
When I run the play button in unity, the cube is created and I receive no errors but the cube does not shrink when I press the down arrow key. However, I know that the size of the vector is decrementing because I tell it to print a message as a test and it does so. But there are no visible changes to the size of the cube in the viewer.
Also, I originally put
currentScale = rubixCube.transform.localScale; in the void Update() function rather than the void Start() and it gave me an error Unassigned reference exception: The variable rubixCube of LetThereBeLight has not been assigned but shouldn't it not matter what function it is in whether it is called upon start or looped over because I already made rubixCube a public GameObject outside of both functions?
Does anyone know how to solve this? Why doesn't this script work?
Two issues:
In the Start you do
GameObject rubixCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
which creates a local variable and hides the class field with equal name rubixCube!
=> The class field rubixCube you later try to use in Update is never assigned!
In Start it should rather simply be
rubixCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
Then in Update you do
currentScale += newScale;
You are changing the field currentScale, which is a Vector3 value ... but that is all.
You are not actually changing the Transform component on the GameObject itself at all, so the cube in your game is not actually being affected at all by this code.
You rather have to actually assign something back to the property
currentScale += newScale;
rubixCube.transform.localScale = currentScale;
I tried to modify your code, I searched for information, and clicked the "CreaterubixCube" button in the OnGUI() function to call the "CreatePrimitive" method, thereby creating a rubixCub object and adding its position attribute. I also just learned unity3d.
people of the stack overflow. I wanted to make a drone game for my personal project for school. Everything has been going well but I am facing a problem now. My script for my drone won't makes the drone stay at a constant height when I press the selected button. In other words when I press E or Q the drone goes continuously up or continuously down. I want it to go up as long as I am pressing the selected key. How would I do that?
code ->
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Levitating : MonoBehaviour
{
Rigidbody ourDrone;
private void Awake()
{
ourDrone = GetComponent<Rigidbody>();
}
// Make the drone default velocity the same value of the gravity acceleration, with opposite direction
private Vector3 DRONE_DEFAULT_VELOCITY = new Vector3(0, 9.81f, 0);
public float upForce;
private void Update()
{
MovementUpDown();
}
private void FixedUpdate()
{
ourDrone.AddRelativeForce(DRONE_DEFAULT_VELOCITY * upForce);
}
void MovementUpDown()
{
if (Input.GetKey(KeyCode.E))
{
upForce = 15;
}
else if (Input.GetKey(KeyCode.Q))
{
upForce = -3;
}
else
{
// Resetting the force muultiplier
upForce = 1;
}
}
}
The problem is that your physics are wrong.
In the default mode (upForce = 1) you're cancelling out gravity so no acceleration will be applied to your rb.
However, when you're pressing one of your control buttons, you're adding a force, and thus, per Newtons law, you're adding an acceleration, which will result in a velocity that will persist even after you've stopped pressing the button.
Now, I don't know exactly why you've modeled it as you have, but the easiest solution to your problem would be to disable gravity on your rigidbody and modify the velocity of the rb directly. Or use a different forceMode in your AddForce. https://docs.unity3d.com/ScriptReference/ForceMode.html use Velocity change.
If you want to model an actual drone, as one would in real life, you should look into control systems, such as PID, but that might be overkill.
Best of luck
I'm facing a problem in my scene. What i'm doing is there are two panels both panel has one button which performing OnClick() listener with the method name RotateCamera().
Also in hierarchy there is a MainMenu gameobject which attached with one script. When I play the scene and click on panel one button than MainCamera rotate with 90 angle to the direction of second panel. Script works fine but I want to smoothly rotate camera to that panel when I click on that button - right now, it is rotating instantly. I don't know what I'm doing wrong on this script.
public class CameraSmooth : MonoBehaviour {
private bool check = false;
private Transform from;
private Transform to;
void Start(){
from = Camera.main.transform;
}
void Update(){
if (to != null) {
Camera.main.transform.rotation = Quaternion.Slerp (from.rotation, to.rotation, 3 * Time.deltaTime);
}
}
public void RotateCamera(){
if (!check) {
Camera.main.transform.Rotate (0,90f,0f);
to = Camera.main.transform;
check = true;
}
else if (check) {
Camera.main.transform.rotation = Quaternion.identity;
to = Camera.main.transform;
check = false;
}
}
}
The main problem here is that you're calling Camera.main.transform.Rotate() in RotateCamera(). This causes the rotation to be applied to the camera immediately, resulting in the instant rotation you're seeing.
Another small misconception - since Transform is a reference type, from and to always actually point to the same instance (the Transform of the camera)! Lastly, the value of 3 * Time.deltaTime will always average around 3/60, and will likely never end up close to 1, which is needed for an interpolation to be complete. As such, the following line:
Camera.main.transform.rotation = Quaternion.Slerp (from.rotation, to.rotation, 3 * Time.deltaTime);
will not be able to do anything even if the first issue is addressed.
The situation calls for a different solution, in my view:
Storing the target rotations in Quaternion variables rather than setting them directly on the Transform, and keeping track of the camera's initial rotation
Maintaining a timer which keeps track of the progress of the rotation (or you can abandon Slerp() and just apply an incremental rotation directly, that's also a valid approach)
Here's my suggested update to your code:
public class CameraSmooth : MonoBehaviour {
private bool check = false;
private float rotationProgress = 1;
private Quaternion initial;
private Quaternion from;
private Quaternion to;
void Start(){
// Cache the starting rotation, so we can calculate rotations relative to it
initial = Camera.main.transform.rotation;
}
void Update(){
if (rotationProgress < 1) {
rotationProgress += 3 * Time.deltaTime;
Camera.main.transform.rotation = Quaternion.Slerp (from, to, rotationProgress);
}
}
public void RotateCamera(){
from = Camera.main.transform.rotation;
rotationProgress = 0;
if (!check) {
to = initial * Quaternion.Euler(0,90f,0f);
check = true;
}
else if (check) {
to = initial;
check = false;
}
}
}
(Haven't tested this code, so please let me know if there are any issues.) Hope this helps! Let me know if you have any questions!
I'd like to create a slider that displays the current frame of an animation within unity, and when you move the slider it updates the current frame within the animation.
So far I have (edited code)
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class NewAnimController : MonoBehaviour {
public Slider sliderScrubber;
public Animator animator;
void Start()
{
float t = animator
.GetCurrentAnimatorStateInfo(0)
.normalizedTime;
//animator.speed = 0.0001f;
//slider.onValueChanged.AddListener(OnValueChanged);
}
public void Update()
{
float animationTime = animator.GetCurrentAnimatorStateInfo(0).normalizedTime;
Debug.Log("animationTime (normalized) is " + animationTime);
sliderScrubber.value = animationTime;
}
public void OnValueChanged(float changedValue)
{
animator.speed = 0.0001f;
animator.Play("Take 001", -1, sliderScrubber.normalizedValue);
}
}
At the moment this changes the current frame in the animation when you adjust the slider, but when the animation is played, the slider doesn't update.
How can I get this to work in both directions?
Many thanks
First be sure to delete all code lines
animation.speed = 0.0001f
which appear to be leftover from a tutorial or debugging.
What about
public void Update()
{
float animationTime = animator.GetCurrentAnimatorStateInfo(0).normalizedTime;
// in case of a looping anime, animationTime = animationTime %1f;
Debug.Log("animationTime is " + animationTime);
// of course remove that in production
slider.value = animationTime;
}
Note of course set the scrubber min/max to 0f, 1f in the editor.
Note, you may prefer to make a "scrubber" for a joint position; also works nice.
Note, subtle issue: experienced programmers may prefer to set the animation speed to zero, while the user's finger is holding down the slider; probably OK without doing this.
NOTE take care to delete all references to animation.speed. It apears to be a carry-over from an incorrect tutorial.
Ok, so after much throwing things around to see what sticks, this is giving me the result I'm after.
I'd like to thank #JoeBlow for all his help.
Somebody buy that guy a drink.
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class NewAnimController : MonoBehaviour {
public Slider sliderScrubber;
public Animator animator;
public void Update()
{
float animationTime = animator.GetCurrentAnimatorStateInfo(0).normalizedTime;
Debug.Log("animationTime (normalized) is " + animationTime);
sliderScrubber.value = animationTime;
}
public void OnValueChanged(float changedValue)
{
animator.Play("Take 001", -1, sliderScrubber.normalizedValue);
}
}
I will update this again when I have a play pause button working and a slider that can change the speed of playback.
I think you almost got it just instead of assigning the value directly in the update function:
sliderScrubber.value = animationTime;
Use this:
sliderScrubber.Set(animationTime, false);
the false parameter is to call the OnValueChange() function in your case you don't want that.
Addionally you can check if the animation is being played to avoid unnecesary calls to the Set function:
http://answers.unity3d.com/questions/362629/how-can-i-check-if-an-animation-is-being-played-or.html
and check your slider.maxValue to adjust it to the animation length.
I'm creating a game in unity 2D and in this game a GameObject called Moving_Truck is required to smoothly move into the scene from that left side. as this will be required later, I tried to make the method run from an other code on another object, the object is called scene control and the script is called opening scene.
the problem is when I push the space button the Moving_Truck game object does not move. I am fairly new to C# and have tried a few solutions such as Vector2.MoveTowards and Vector2.Lerp. I have also modified my code multiple times trying to get this to work. here is the most recent version of the codes:
CharacterBase
using UnityEngine;
using System.Collections;
public class CharacterBase : MonoBehaviour {
private float SprSize, HalfSprSize, Distance;
public int run = 1;
public void CharMove(int Dir, string Char, float speed, string BackgroundName)
{
var CharOBJ = GameObject.Find(Char);
var BGOBJ = GameObject.Find(BackgroundName);
SprSize = CharOBJ.GetComponent<Renderer>().bounds.size.x;
HalfSprSize = SprSize / 2;
Vector2 EndPos = new Vector2(BGOBJ.transform.position.x, CharOBJ.transform.position.y);
Debug.Log(EndPos);
CharOBJ.transform.position = Vector2.MoveTowards(CharOBJ.transform.position, EndPos, speed * Time.deltaTime);
}
}
OpeningScene
using UnityEngine;
using System.Collections;
public class OpeningScene : CharacterBase {
int Advance = 0, Run = 0;
void Start ()
{
}
void FixedUpdate()
{
if (Input.GetKeyUp("space"))
{
Run = 1;
Debug.Log("Space Pressed");
}
if (Run == 1)
{
Run = 0;
Advance += 1;
switch (Advance)
{
case 1:
CharMove(-1, "Moving_Truck", 0.05f, "House_Front");
break;
case 2:
CharMove(1, "Moving_Truck", 0.05f, "House_Front");
break;
}
}
}
}
This is driving me nuts, I've been trying to fix it for about an hour or Two now, can someone please help, also sorry for the long question, just comment if you need more info. also please ignore the Dir Argument for now.
Thanks.
Unity's Input.GetKeyUp only returns true on the frame when you release the spacebar. Because of this, CharMove will only be called that one frame you press the spacebar, and then only move 0.05f * timeDelta, which is probably going to be less than a pixel.
Also, this is unrelated, but you don't want to call GameObject.Find(string) every time you move the character. Instead, call it once in the Start() method and then store the result to a field.
Have you tried
GetComponent<Rigidbody2D> ().velocity = new Vector2 (moveSpeed, GetComponent<Rigidbody2D> ().velocity.y);