Why cant I modify Transform.position? - c#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class MoveAbleButtonSystem : MonoBehaviour
{
public Transform ObjectToMove;
public bool moveLeft = true;
public bool moveRight = false;
public float SpeedToMove = 1f;
private bool pressed = false;
public float minZ = 0f;
public float maxZ = 0f;
void OnMouseDown()
{
pressed = true;
}
void OnMouseUp()
{
pressed = false;
}
void Update()
{
Transform T = ObjectToMove.transform;
if (pressed && moveLeft)
{
T.Translate(Vector3.forward * SpeedToMove * Time.deltaTime, Space.World);
}else
if(pressed && moveRight)
{
T.Translate(Vector3.back * SpeedToMove * Time.deltaTime, Space.World);
}
if(T.position.z > maxZ)
{
T.position.z = maxZ;
}
}
}
Assets\Scripts\MainMenu\MoveAbleButtonSystem.cs(42,13): error CS1612: Cannot modify the return value of 'Transform.position' because it is not a variable
I dont get why I cannot change the position. All I want to do is for the ObjectToMove to move until it passes a certain point, after what it is not supposed to move anymore. Using Clamp provides the same error.

The following simplified example demonstrates the same compile error:
public class Program
{
public static void Main()
{
var container = new Container();
container.Property.Value = 1; // Error CS1612 - Cannot modify the return value of 'Container.Property' because it is not a variable
}
}
public struct Item
{
public int Value;
}
public class Container
{
public Item Property { get; set; }
}
This is the issue you're seeing. It's happening because the Container.Property property is returning a copy of a value type, in this case struct Item.
Because this is a copy of the item stored in Container and not the item itself, changing the copy's properties cannot have any visible effect because the value returned from the property is not stored anywhere - no variable is assigned from it.
Therefore the compiler gives an error, to avoid misleading and useless code.
The documentation for Error CS1612 explains all this in detail.

Others - and the error documentation - already explained the issue and the why in detail.
But in terms of a solution for your issue you would go e.g.
// Get copy of current position
var position = T.position;
if(position.z > maxZ)
{
// modify the copy
position.z = maxZ;
}
// assign back / apply modified value
T.position = position;
You could even combine and simplify it a lot by doing e.g.
public enum MoveDirection
{
Left,
Right
}
public MoveDirection direction;
void Update()
{
if(pressed)
{
var T = ObjectToMove.transform;
var position = T.position;
var multiplier = direction == MoveDirection.Left ? 1 : -1;
position.z = Mathf.Clamp(position.z + SpeedToMove * Time.deltaTime * multiplier);
T.position = position;
}
}

As pointed in the comments from Mathieu and Matthew, position is a struct field of the calss Transform and due to this reason you can not change its fields like this. If you want to understand why this is not working imagine that Unity's Transform and position objects look something like this:
struct Position
{
public int x;
public int y;
public int z;
}
class Transform
{
public Position _position;
public Position position
{
get
{
return _position;
}
set
{
_position = value;
}
}
}
When you write T.position.z the position part is actually a value copy of the original T's position variable returned by the property getter. So by changing the copy of position you will not get the desired effect of updating the T.position's z field. Due to this reason C# it trying to help you by throwing an error in order to prevent a bug that is very hard to identify.

You can not change the individual components of a Vector3. To do this, you will have to change all the components. i.e :
if (T.position.z > maxZ)
{
T.position = new Vector3(T.position.x, T.position.y, maxZ)
}

Related

Assigning Custom AABB To game objects

I'm trying to set up custom mathematic code in my Unity project to test for collision using AABB.
So far, I'm pretty sure I have the code just to calculate if two boxes are colliding, but not sure how I'd essentially attach them to a game object to test if they are colliding.
Here's the code I have currently.
The LineIntersection and IntersectingAxis functions can be ignored I believe, it's just part of my class.
public class AABB
{
public AABB(Vector3 Min, Vector3 Max)
{
MinExtent = Min;
MaxExtent = Max;
}
//This could be a Vector2 if you want to do 2D Bounding Boxes
Vector3 MinExtent;
Vector3 MaxExtent;
public float Top
{
get { return MaxExtent.y; }
}
public float Bottom
{
get { return MinExtent.y; }
}
public float Left
{
get { return MinExtent.x; }
}
public float Right
{
get { return MaxExtent.x; }
}
public float Front
{
get { return MaxExtent.z; }
}
public float Back
{
get { return MinExtent.z; }
}
public static bool Intersects(AABB Box1, AABB Box2)
{
return !(Box2.Left > Box1.Right
|| Box2.Right < Box1.Left
|| Box2.Top < Box1.Bottom
|| Box2.Bottom > Box1.Top
|| Box2.Back > Box1.Front
|| Box2.Front < Box1.Back);
}
}
And in another script called AABBCollider I have the following code
[RequireComponent(typeof(MatrixController))]
public class AABBCollider : MonoBehaviour {
private MatrixController MCCache;
public Vector3 CubeSize = new Vector3(1,1,1);
public VectorAlgorithm.AABB aabb;
public VectorAlgorithm.AABB aabb2;
//public VectorAlgorithm.AABB other;
public bool Intersect(AABBCollider other)
{
return VectorAlgorithm.AABB.Intersects(aabb, other.aabb);
}
// Use this for initialization
void Start () {
MCCache = GetComponent<MatrixController>();
//aabb = new VectorAlgorithm.AABB(MCCache.position - CubeSize, MCCache.position + CubeSize);
}
// Update is called once per frame
void Update () {
aabb = new VectorAlgorithm.AABB(MCCache.position - CubeSize, MCCache.position + CubeSize);
Debug.Log(VectorAlgorithm.AABB.Intersects(aabb, aabb));
}
}
The AABBCollider Script it attached to my 'PlayerCharacter' and 'Enemy' game objects. All I'm trying to do is print to the console either true or false if they are colliding or not.
I'm pretty sure aabb is 'attached???' to one game object, so doing (aabb, aabb) just returns true, which I'm not after.
If they collide, print TRUE to the console, if they're not colliding, print FALSE to the console.
At this stage, I'm not too worried about pushing them apart if they collide, just want to know my function is working.

Nvidia Flex: Why can't I set particle count in FlexArray Actor? (Unity)

Im new to coding in C# and I'm not able to set the particle count in the Nvidia FlexArray script, the particle count public class appears grey and can't be edited. It is somehow permanently set to 0.
Ive attached an image and the script that comes with the package. Id greatly appreciate any help. The image for the inspector view on array actor
using UnityEngine;
namespace NVIDIA.Flex
{
[ExecuteInEditMode]
public class FlexArrayAsset : FlexAsset
{
#region Properties
public Mesh boundaryMesh
{
get { return m_boundaryMesh; }
set { m_boundaryMesh = value; }
}
public Vector3 meshLocalScale
{
get { return m_meshLocalScale; }
set { m_meshLocalScale = value; }
}
public float meshExpansion
{
get { return m_meshExpansion; }
set { m_meshExpansion = value; }
}
public float particleSpacing
{
get { return m_particleSpacing; }
set { m_particleSpacing = Mathf.Max(value, 0.01f); }
}
public float particleCount
{
get { return m_particleCount; }
set { m_particleCount = Mathf.Max(value, 516f); }
}
#endregion
#region Methods
#endregion
#region Messages
#endregion
#region Protected
protected override void ValidateFields()
{
base.ValidateFields();
m_particleSpacing = Mathf.Max(m_particleSpacing, 0.01f);
}
protected override void RebuildAsset()
{
BuildFromMesh();
base.RebuildAsset();
}
#endregion
#region Private
void BuildFromMesh()
{
if (m_boundaryMesh)
{
Vector3[] vertices = m_boundaryMesh.vertices;
if (vertices != null && vertices.Length > 0)
{
for (int i = 0; i < vertices.Length; ++i)
{
Vector3 v = vertices[i];
vertices[i] = new Vector3(v.x * m_meshLocalScale.x, v.y * m_meshLocalScale.y, v.z * m_meshLocalScale.z);
}
int[] indices = m_boundaryMesh.triangles;
if (indices != null && indices.Length > 0)
{
FlexExt.Asset.Handle assetHandle = FlexExt.CreateRigidFromMesh(ref vertices[0], vertices.Length, ref indices[0], indices.Length, m_particleSpacing, m_meshExpansion);
if (assetHandle)
{
FlexExt.Asset asset = assetHandle.asset;
FlexExt.Asset particlesOnly = new FlexExt.Asset();
particlesOnly.numParticles = asset.numParticles;
particlesOnly.maxParticles = asset.numParticles;
particlesOnly.particles = asset.particles;
StoreAsset(particlesOnly);
FlexExt.DestroyAsset(assetHandle);
}
}
}
}
}
[SerializeField]
Mesh m_boundaryMesh = null;
[SerializeField]
Vector3 m_meshLocalScale = Vector3.one;
[SerializeField, Tooltip("Particles will be moved inwards (if negative) or outwards (if positive) from the surface of the mesh according to this factor")]
float m_meshExpansion = 0.0f;
[SerializeField, Tooltip("The spacing used for voxelization, note that the number of voxels grows proportional to the inverse cube of radius, currently this method limits construction to resolutions < 64^3")]
float m_particleSpacing = 0.1f;
#endregion
}
}
Properties can't be serialized directly in Unity.
Recommended flow would be to mark m_particleCount as a [SerializeField], which exposes it to the editor even if it's private.
It looks like it might be buried in the Flex API though, so I'm not sure if you can edit it directly. A more complex editor script could do the job in such a case, I'd recommend looking at trying to hack the value with your own value, then using OnValidate() to write that value to the real one.
That said, is this value really what you think it is? I haven't used the API but 'particleCount' is usually the number of live particles in the system, naturally at edit time it would be 0, but at runtime it could be 10 or 20 or 1000 depending on what the particle system is doing.

How can I keep the originalSpeed but also to be able to change for a new speed?

public class EnemyBehaviour : MonoBehaviour
{
public float speed; // or the speed of rotation.
public bool randomSpeed = false;
public float speedRange = 4;
private float originalSpeed;
private void Start() { originalSpeed = speed; }
private void Update()
{
if (!randomSpeed)
{
speedRange = 0;
speed = originalSpeed;
}
else speed = Random.Range(1, speedRange);
}
}
The problem is in Update at this part:
if (!randomSpeed)
{
speedRange = 0;
speed = originalSpeed;
}
If for example when running the game the speed was 3 now when I set it to false the speed value will be 3 all the time I can't change it now. But I want to be able to change the speed value to any value. I want that it will get back to the originalSpeed if I set to false but also to be able to change for a new speed and then update the originalSpeed to the new one.
The way I did it now I locked the speed when it's false.
Surely it will lock the speed as you assign the value of speed by originalSpeed every frame.
To solve it, you should not put things inside the Update(). Also, try to use 'get set' syntax for those protected public variable.
Lets say you want to assign a value to the 'speed' only one time, when you click the randomSpeed checkbox.
private float randomSpeed
public float RandomSpeed
{
get {return randomSpeed;}
set {
randomSpeed = RandomSpeed;
if (!randomSpeed){
speed = originalSpeed;
} else {
speed = Random.Range(1, speedRange);
}
}
}

Error CS0120 in Unity5

I'm working on Stealth tutorial on Unity5. While I was writing the "Alarm Light" script, this error showed up
Assets/AlarmLight.cs(28,31): error CS0120: An object reference is required to access non-static member `UnityEngine.Light.intensity'
Here is the entire script;
using UnityEngine;
using System.Collections;
public class AlarmLight : MonoBehaviour {
public float fadeSpeed = 2f;
public float highIntensity = 2f;
public float lowIntensity = 0.5f;
public float changeMargin = 0.2f;
public bool alarmOn;
private float targetIntensity;
void Awake(){
GetComponent<Light>().intensity = 0f;
targetIntensity = highIntensity;
}
void Update()
{
if (alarmOn) {
GetComponent<Light>().intensity = Mathf.Lerp (GetComponent<Light>().intensity, targetIntensity, fadeSpeed * Time.deltaTime);
CheckTargetIntensity ();
}
else
{
Light.intensity = Mathf.Lerp (GetComponent<Light>().intensity, 0f, fadeSpeed * Time.deltaTime);
}
}
void CheckTargetIntensity (){
if (Mathf.Abs (targetIntensity - GetComponent<Light>().intensity) < changeMargin) {
if (targetIntensity == highIntensity) {
targetIntensity = lowIntensity;
}
else {
targetIntensity = highIntensity;
}
}
}
}
Basically, what the compiler is telling you is that you're trying to use an instance member like a static member, which obviously, is incorrect.
Look at this line in your code
else {
Light.intensity = Mathf.Lerp (GetComponent<Light>().intensity, 0f, fadeSpeed * Time.deltaTime);
}
On the right hand side, you use GetComponent<Light>().intensity, which is the correct way of accessing a single Light's intensity.
On the LEFT hand side, however, you're using Light.intensity. The Light class does not have any static member named intensity, and hence the error.
Change your code to
else {
GetComponent<Light>().intensity = Mathf.Lerp (GetComponent<Light>().intensity, 0f, fadeSpeed * Time.deltaTime);
}
and your error should go away.
Think about it this way. You can change the intensity of each of your lights seperately, correct? Therefore, it MUST be a member of an instance of the class, rather than the class itself.
If changing a single value effects everything that uses it, (such as Physics.gravity), then those are static members of the class. Keep that in mind, and you won't have this issue pop up.

C# class organisation / structure - generalising a class performing a looping linear interpolation

I'm working on a C# XNA game, and have the following code which updates a Sprite's alpha level based on the result of a linear interpolation:
class AlphaProcess : GameProcess {
Sprite sprite;
public Sprite Sprite {
get { return sprite; }
set { sprite = value; }
}
float start;
public float Start {
get { return start; }
set { start = value;}
}
...<snip>...
public override void Update(float elapsed) {
if (FirstUpdate) {
Start = sprite.Alpha;
FirstUpdate = false;
}
Time += elapsed;
if (Time >= Duration)
Finished = true;
sprite.Alpha = MathHelper.Lerp(start,end,Time/Duration);
}
}
'Start' (float), 'FirstUpdate' (bool), 'Time' (float), 'Duration' (float) are all fields of class AlphaProcess. 'Sprite' is a class containing details such as scale, position, direction, rotation etc.
The 'Update' method is called 60 times a second, calculating a new alpha value for the sprite over a specified time period. When it's done the GameProcess is removed from a queue.
This code works fine, however it's very specific. The Sprite class I have contains lots of properties that it would be handy to lerp over a fixed period of time (rotation, position (for easing in and out)). Many of these variables are also floats. It seems silly to create multiple 'GameProcess'-extending classes that do pretty much the same thing, just on a different variable. However, I can't think of how to best refactor this so that I can just have a pointer to a float value being modified, rather than the specific sprite and it's alpha / rotation / scale / whatever value.
I've been through the C# reference / value / parameter passing questions on SO and know in C# you can't store a reference as a field. The 'GameProcessQueue' which calls Update knows nothing of the internals of the 'GameProcess' being updated. Is there a smarter way of doing this so I can generalise / abstract the class to update a field of another class (like here, with the 'Alpha' field of 'Sprite')?
Your main problem is that your AlphaProcess class has low cohesion. It has three main roles, Tweening, Lerping and getting/setting a Sprite Alpha Value. To improve your design, you need to split these into separate decoupled classes.
Goal: Turn your AlphaProcess into a TweenProcess.
Instead of passing it a sprite, pass it an interface through which it can get and set the float it wishes to operate on. And instead of calling Lerp directly, pass it an interface or delegate to a Tween function. (I've used an interface instead of a delegate because I've found delegates often create garbage which can cause your game to stutter.)
The tweening class is:
interface IFloatPropertySource
{
float FloatProperty { get; set; }
}
interface ITweenFunction
{
float Tween(float start, float end, float t);
}
class TweenProcess : GameProcess
{
float start;
IFloatPropertySource floatSource;
ITweenFunction tweenFunction;
public TweenProcess(IFloatPropertySource floatSource, ITweenFunction tweenFunction)
{
this.floatSource = floatSource;
this.tweenFunction = tweenFunction;
}
public override void Update(float elapsed) {
if (FirstUpdate) {
start = floatSource.FloatProperty;
FirstUpdate = false;
}
Time += elapsed;
if (Time >= Duration)
Finished = true;
floatSource.FloatProperty = tweenFunction.Tween(start, end, Time / Duration);
}
}
The class to get/set the alpha is:
class SpriteAlphaSource : IFloatPropertySource
{
Sprite sprite;
public SpriteAlphaSource(Sprite sprite)
{
this.sprite = sprite;
}
public float FloatProperty
{
get
{
return sprite.Alpha;
}
set
{
sprite.Alpha = value;
}
}
}
I know you're sort of trying to avoid having this class. But all the ways around it, like via reflection are really expensive. In a project that has more renderables than just sprites, you may want to decouple it from sprite and make it operate on a base interface like IRenderable instead.
The lerping mechanism is:
class Lerp : ITweenFunction
{
public float Tween(float start, float end, float t)
{
return MathHelper.Lerp(start, end, t);
}
}
Basically, you pass a callback function into the Update class-- in C# terms, you use a delegate function. Add a property to your class that looks like
public delegate SetThis(float value) { get; set; }
To set that, in client code you do something like
myAlphaProcess.SetThis = ( x => sprite.Alpha = x; )
And in the Update() function, you have
SetThis(MathHelper.Lerp(start,end,Time/Duration));
The syntax in this answer is approximate, because I don't have a compiler at this computer, but this is how you do it. Good google searches would be "lambda C#" or "delegate C#".
This can be a generic approach to that matter... maybe you should do a TweenManager and add the Tweenables to a collection for update only active Tweenables...
This should be right, but I have not checked it ... maybe generics give any problem... with calcs, but should be avoided with the generic delegate... :)
Tweenable<float> Alpha = new Tweenable(defaultvalue);
public class Tweenable<T>
{
public T Value {
get { return Function(elapsed, source, target-source, duration);}
private set { source = value; elapsed = 0;}
}
public TweeningFunction<T> Function;
float elapsed, duration;
T source, target;
public void Update(float Elapsed)
{
if (elapsed<duration)
{
elapsed+= Elapsed;
if (elapsed > duration) elapsed = duration;
}
}
public void Start(T Source, T Target, float Duration)
{
...
}
}
public delegate T TweeningFunction( float timeElapsed,
T start,
T change,
float duration );
public static partial class Tweening {
public static class Quartic {
public static float EaseIn( float t, float b, float c, float d ) {
return c * (t /= d) * t * t * t + b;
}
public static float EaseOut( float t, float b, float c, float d ) {
return -c * ((t = t / d - 1) * t * t * t - 1) + b;
}
public static float EaseInOut( float t, float b, float c, float d ) {
if ( (t /= d / 2) < 1 ) {
return c / 2 * t * t * t * t + b;
}
return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
}
}
}

Categories

Resources