Unity general questions

Comments

  • edited
    @Thelangfordian the problem isn't in doing barrel rolls, my current setup already allows for that. The problem I'm trying to solve for is to correct for rolling when my controls are in neutral - so a self-correcting force is what I'm looking for, not for the player to be able to counter the rotation manually. Does that make sense?

    My code that I attached already does do that, but it also has some unforeseen weird vector force in there that makes it accelerate in odd directions when it shouldn't (it should just be purely rotational, no acceleration).

    So how do you self-correct a torque in one axis? I think answering that alone will do.
  • edited
    @Tuism, Ah ok. I see.

    To reduce the torque you would need to add a counter torque (with dampingFactor) until the rigidbody.angularVelocity is zero. To apply this to a single axis you need to convert the angularVelocity to local coordinates, apply dampingFactor, then convert back to global coordinates.

    I tried the following, seems to work. :/

    if(noUserInput) {
                if(rigidbody.angularVelocity != Vector3.zero) {
    
                    Vector3 localAngularVelocity = transform.InverseTransformVector(rigidbody.angularVelocity);
    
                    // Apply damping factor to desired local axis
                    localAngularVelocity.y *= -0.85f;
                    localAngularVelocity.x = 0f;
                    localAngularVelocity.z = 0f;
    
                    Vector3 globalAngularVelocity = transform.TransformVector(localAngularVelocity);
    
                    myRigidbody.AddTorque(globalAngularVelocity);
                }
            }
  • This is a very, very, very basic question, but I don't even know what to google :P

    If I want to make some script/function that I can use in another script by just including it into a script with "using" at the top, how do I do that? Is that a good way of making repeat-use functions?
  • edited
    You need to get an instance of the class that you want to call a function from with GetComponent, so you could have a script attached to your camera and then call Camera.main.GetComponent<MyScript>().MyFunction(). The other way is to declare the function in a class as public static, then you can call it from anywhere with MyClass.MyStaticFunction(), but you can't access instance specific variables or functions from that function. If you just want to use declarations from another namespace, you would use using.

    Thanked by 1Tuism
  • edited
    A question that's probably damn easy but I don't know what to google for :/

    I have this code to check if a point is within in the camera view. Sounds good:

    public static bool InfiniteCameraCanSeePoint (Camera camera, Vector3 point) 
    	{
    		Vector3 viewportPoint = camera.WorldToViewportPoint(point);
    		return (viewportPoint.z > 0  (new Rect(0, 0, 1, 1)).Contains(viewportPoint));
    	}


    But I get this error and I'm not sure why:
    Assets/Scripts/Func.cs(63,29): error CS0119: Expression denotes a `value', where a `method group' was expected
    The "Contains" is highlighted in monodevelop.

    Help? Thanks!
  • This part doesn't look right. Something missing after the 0 but I'm not too sure what you want to be there.
    return (viewportPoint.z > 0  (new Rect(0, 0, 1, 1)).Contains(viewportPoint));


    Probably this?
    return (viewportPoint.z > 0 &&  (new Rect(0, 0, 1, 1)).Contains(viewportPoint));
    Thanked by 1Tuism
  • Zaphire said:
    This part doesn't look right. Something missing after the 0 but I'm not too sure what you want to be there.
    return (viewportPoint.z > 0  (new Rect(0, 0, 1, 1)).Contains(viewportPoint));


    Probably this?
    return (viewportPoint.z > 0 &&  (new Rect(0, 0, 1, 1)).Contains(viewportPoint));
    Thanks for this correction, the code runs, but it doesn't seem to work as intended.

    The two lines together are supposed to return whether a point in world space is within the camera's view.

    I don't understand the syntax of the second line of code at all, is it possible to explain it to me? (I have literally no idea what to google for :/ )
  • I don't think that code will work for 3d objects because the new Rect only checks for objects within 0 to 1 ranges where the 3d objects might be X = 200.

    Have a look at this link as I think it will work for what you are trying to do.
    https://docs.unity3d.com/ScriptReference/GeometryUtility.TestPlanesAABB.html
    Thanked by 1critic
  • At a glance, the code doesn't look wrong.

    camera.WorldToViewportPoint should give you a point in your viewport space, where bottom left is (0,0), top right is (1,1), and the z coordinate is the distance in front of you. So when you're checking if something's within the camera view, you're checking if the z is positive (i.e. it's in front of your camera rather than behind it), and that the x and y each fall between 0 and 1.

    I'd debug it by printing out what viewportPoint is giving you, and seeing why it is or isn't falling within what you expect.
    Thanked by 1Tuism
  • Code works fine for me, maybe you are not giving it the right camera or the right position?

    image

    using UnityEngine;
    using UnityEngine.UI;
    
    public class cube : MonoBehaviour {
    
        public RawImage indicator;
    
    	// Use this for initialization
    	void Start () {
    		
    	}
    	
    	// Update is called once per frame
    	void Update ()
        {
            if (InfiniteCameraCanSeePoint(Camera.main, this.transform.position))
                this.indicator.color = new Color32(19, 176, 66, 255);
            else
                this.indicator.color = new Color32(197, 23, 23, 255);
        }
    
        public static bool InfiniteCameraCanSeePoint(Camera camera, Vector3 point)
        {
            Vector3 viewportPoint = camera.WorldToViewportPoint(point);
            return (viewportPoint.z > 0 && (new Rect(0, 0, 1, 1)).Contains(viewportPoint));
        }
    }
    Thanked by 1Tuism
  • Rad, thanks for trying it out, I'll check if I've fucked up on the implementation a bit later, busy with something else now :)
    Thanked by 1critic
  • What I can see might need to be added to @critic example is to factor in half the width/height of the object as it only triggers if the centre of the object is within view of the camera
    Thanked by 1critic
  • Hey all,

    Im trying to get a little system in place where I record the input actions of a player at each frame so that I can effectively play back what the user has inputted. The logic basically looks like this (pseudo code because the actual implementation is a bit more complicated).

    float MoveSpeed = 10f;
    
        int _frame = 0;
        Vector2 _currentDirection;
        Dictionary<int, Vector2> _history;
    
        void FixedUpdate()
        {
            Vector2 newDirection;
            if (InputEnabled)
            {
                if (Input.KeyDown(KeyCode.UpArrow))
                {
                    newDirection.y = 1;
                }
    
                if (Input.KeyDown(KeyCode.DownArrow))
                {
                    newDirection.y = -1;
                }
    
                // --- Repeated for other inputs
    
                if (newDirection != _currentDirection)
                {
                    _history.Add(_frame, newDirection);
                    _currentDirection = newDirection;
                }
    
                Tick(_frame);
    
                _frame++;
            }
            else
            {
                // Logic for playback
            }
        }
    
        void Tick(int frame)
        {
            if (_history.ContainsKey(frame))
            {
                _currentDirection = _history[frame];
            }
    
            GetComponent<Rigidbody2D>().velocity = _currentDirection * MoveSpeed;
        }


    This works fine and if I set the player back to their original starting point and playback the inputs, it works as expected. The problem I have is that I what to be able to set an arbitrary frame at any given time, and need to be able to calculate the position of the gameObject at that point in time. Any suggestions?

    If need be, I can post my actual code if it will make things easier.
  • I think the problem you're going to have with an "arbitrary frame" is that you're relying on physics for movement, so your movement is inherently non-deterministic. So let's say you want to jump to frame 50, that is going to be close to impossible (without literally running through the previous steps beforehand) because at that point the body may have still had velocity from frame 49.
    You've got 2 solutions that I can think of:

    1) Stop using the built in physics and write a movement controller yourself. This would involve stuff that calculates the velocity and momentum manually and moves the transform absolutely each from based on that. Once you've done this, you can loop through all steps in an instant to find the velocity and transform position that it would have had at frame 50, because you aren't relying on the real stuff happening in the scene. The issue here is that if there was something in the world that would have effected the object (like hitting a wall) it'll be very hard to take into account unless you're also doing your own collision detection.

    2) This is probably more hacky, and I'm not 100% sure that the body will always end at the exactly the right point. When the player clicks "skip to frame 50", you change timescale to like 1000% and let stuff actually play through behind a loading screen (or show it for a fun effect), then at frame 50 you set timescale back to 1. I can't remember what timescale does to FixedUpdate (i.e. I can't remember if timescale of 2 will also make FixedUpdate run at 120hz, but I'm going to assume it does), so you need to just double check that it is running as it should.
    Thanked by 1CiNiMoD
  • Stop using the built in physics and write a movement controller yourself
    This is my current thinking, but I was hoping there was a better way.
    When the player clicks "skip to frame 50", you change timescale to like 1000% and let stuff actually play through behind a loading screen (or show it for a fun effect)
    This though! This sounds like it solves my problem and looks like a feature :P
  • CiNiMoD said:
    This though! This sounds like it solves my problem and looks like a feature :P
    For extra points, use it with a post effect like: https://github.com/staffantan/unity-vhsglitch
    And sound effect like: https://freesound.org/people/tim.kahn/packs/2507/

  • CiNiMoD said:
    Stop using the built in physics and write a movement controller yourself
    This is my current thinking, but I was hoping there was a better way.

    [quote]When the player clicks "skip to frame 50", you change timescale to like 1000% and let stuff actually play through behind a loading screen (or show it for a fun effect)
    This though! This sounds like it solves my problem and looks like a feature :P[/quote]

    If only the physics engine was that reliable. In almost 100% of the cases, whenever I mess with the timescale, the physics play out differently. I know about delta time and fixedupdate and all that jazz, so it's not a mistake in the code. Changing the timescale will almost always resolve physics differently. Over short times, it's not a big deal. Over longer periods it'll break with 90% certainty.

    So if precision actual playback is desired, I would record the actual positions and go from there rather than recording input "moments".
  • Oh, one other thing you can do alongside what you had when you first posted: With every frame, just record the .velocity too. And when skipping to that frame, just set .velocity. Not entirely sure why I didn't think of that before.
  • This is a somewhat convoluted question... So let me try and set it up:

    I have a bunch of waypoints setup in a scene. They're all trigger colliders. I want to join them up in their code - the premise being that two waypoints that are joined holds a reference to one another in an array.

    For me to do that, currently I have to click the waypoint that I want to change, then click on another waypoint to see what its name is (because they're visually identical), and then return to the first waypoint, and drag the destination into the inspector reference field.

    So I want to simplify that stupendously lengthy process, are any of these possible through editor code or something?
    1. Can I drag an object from the visual scene (not hierarchy) into the reference?
    2. Can I drag from a reference field to an object in the visual scene to connect it?
    3. Can I drag lines between objects to signify that they're connected, then let the code work out those connections?

    Does this make sense? :)
  • I think all three are possible, though at different levels of difficulty. (It's been years since I wrote any Unity editor tools, so my knowledge may be outdated.)

    I think the easiest way for what you're doing is to have a button in an editor window or custom inspector that assigns to a reference field based on your selection. With the editor window, you can make it a MenuItem and assign it a shortcut key combination too.

    Doing 3 is next, but I think it requires you to write some code to handle detecting what your mouse is hovering over (which might need colliders, not sure).

    Doing 1 or 2 requires that too, but I think it also needs you to write a custom inspector that handles new events for dragging.

    That latter ones are better UX, but unless you're trying to sell a tool, I don't think they're worth the effort (except to learn more about editor tools).
    Thanked by 1Tuism
  • @elyaradine thanks for that analysis :D Do you know which functions/syntax/words I should go google for to do something like this?

    I agree that it's not about super slick UX, but what I do want at least is to perhaps see connections between nodes with lines in the editor window so I can see where connections are to make debugging less painful...
  • edited
    Here's something that seems should be trivial...

    I'm making a touch game, the left side controls heading with a joystick mechanism, the right side are some buttons that controls speed.

    When I touch and drag the left, it works just fine.
    Whenever I touch the screen again, the first touch "cancels", or gets interrupted. Debugging says that the touch is still registering as Moved or Stationary, but the movement stops. Not sure why that is?

    for (int i = 0; i < Input.touches.Length; i++)
    	{
    		if (Input.GetTouch(i).position.x < Screen.width/2)
    		switch (Input.GetTouch(i).phase) 
    		{
    		case TouchPhase.Began:
    			// code to run when touch begins here...
    			controlType = UseControlType.Mouse;
    			touchPointStart = Input.GetTouch(i).position;
    			if (EventVR.activeSelf)
    			{
    				EventMouseOn();
    			}
    			flyFingerId = Input.GetTouch(i).fingerId;
    			break;
    		case TouchPhase.Moved:
    		case TouchPhase.Stationary:
    			// code to run when touch is being dragged here...
    			if (flyFingerId == Input.GetTouch(i).fingerId)
    			{
    				touchPointOffset = touchPointStart - new Vector2(Input.mousePosition.x, Input.mousePosition.y);
    				touchPointOffset = touchPointStart - new Vector2(Input.GetTouch(i).position.x, Input.GetTouch(i).position.y);
    			}
    			break;
    		case TouchPhase.Ended:
    		case TouchPhase.Canceled:
    			if (flyFingerId == Input.GetTouch(i).fingerId)
    			{
    				flyFingerId = -1;
    				touchPointStart = Vector2.zero;
    			}
    			break;
    	}
    	}
  • edited
    *Edit, missed the if that checks that the touch was on the left side of the screen. Ignore the below if that check does work, which I see no reason as to why it wont.

    I assume the flyFingerId is what you use to keep track of the left finger that controls the joystick?

    In that case, if you press with your second finger, that touch will be in touchphase began, and you do not check if the flyFingerId == -1 before assigning it.
    So you will overwrite your flyFingerId with the new second touch. So maybe an if check in there before assigning the touchPointStart and the flyFingerId.
  • I don't know the answer (I haven't worked with mobile touch controls in years), but I do know that @Dipso has made a mobile touch controls package in the past (which you can grab here). I don't know if it's old and incompatible with the new Unity versions though, but it might be helpful to compare.
  • @Tuism, I only had a quick look at it but if you are using touchPointOffset to do the actual movement it will cause issues. touchPointOffset will have the offset value for whichever finger was stationary last in the list of touches from Input.GetTouch().
  • @Tuism, I only had a quick look at it but if you are using touchPointOffset to do the actual movement it will cause issues. touchPointOffset will have the offset value for whichever finger was stationary last in the list of touches from Input.GetTouch().
    So I stared at what you said for a while and tried to figure out what you mean, and I took out the mouse bit which I think would cause problems, but it still behaves the same, can you elaborate on what you meant there? Can it be fixed to not do that?

    I thought by using fingerId I was able to identify and lock onto the finger that initially started the touch?
  • edited
    So Im trying to make a camera follow a gameObject. This object can be switched out. I have something working, but its a bit jittery. Here is the code that handles the right side:

    public GameObject Following;
        
        private Camera _camera;
        private float _aspectRatio;
    
        void Start()
        {
            _camera = GetComponent<Camera>();
            _aspectRatio = Screen.width / Screen.height;
        }
    
        void Update()
        {
            if (!Following)
            {
                return;
            }
    
           var pos = _camera.WorldToViewportPoint(Following.transform.position);
    
            if (pos.x > 0.8)
            {
                var delta = (pos.x - 0.8f) * _aspectRatio;
                transform.position += new Vector3(delta, 0, 0);
            }
        }


    And the result is this (not the best gif, but you can see the stutter on the edge):

    image

    EDIT: So Im watching a movie with the kids and playing around with some numbers. I have got it working fine for 16:9 aspect ratio, but the _vibrating_ object still happens on the X-axis when testing on 5:4 or 4:3.

    Here is the updated code:
    public GameObject Following;
    
        private Camera _camera;
        private Vector3 _delta;
        private Vector3 _velocity;
    
        void Start()
        {
            _camera = GetComponent<Camera>();
        }
    
        void Update()
        {
            if (!Following)
            {
                return;
            }
    
            var pos = _camera.WorldToViewportPoint(Following.transform.position);
            _delta.x = pos.x > 0.9 ? (pos.x - 0.9f) * 40f : pos.x < 0.1 ? (pos.x - 0.1f) * 40f : 0f;
            _delta.y = pos.y > 0.9 ? (pos.y - 0.9f) * 15f : pos.y < 0.1 ? (pos.y - 0.1f) * 15f : 0f;
    
            transform.position = Vector3.SmoothDamp(transform.position, transform.position + _delta, ref _velocity, 0.01f);
        }


    Any ideas how I calculate the value I need to multiply the deltas by?
  • I found the answer.

    tl;dr I need to calculate the delta in world space:

    public GameObject Following;
    
        private Camera _camera;
        private Vector3 _delta;
        private Vector3 _worldSize;
    
        void Start()
        {
            _camera = GetComponent<Camera>();
            _worldSize = _camera.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 10)) * 2;
        }
    
        void Update()
        {
            if (!Following)
            {
                return;
            }
    
            var pos = _camera.WorldToViewportPoint(Following.transform.position);
            _delta.x = pos.x > 0.9 ? (pos.x - 0.9f) * _worldSize.x : pos.x < 0.1 ? (pos.x - 0.1f) * _worldSize.x: 0f;
            _delta.y = pos.y > 0.9 ? (pos.y - 0.9f) * _worldSize.y : pos.y < 0.1 ? (pos.y - 0.1f) * _worldSize.y : 0f;
    
            transform.position += _delta;
        }


    This still has a flaw in that it will have less tolerance on the Y-axis because its smaller, but thats pretty easy to solve now.
  • edited
    You can try this, it's aspect ratio independent and doesn't involve conditionals, not 100% sure if I'm using the MoveTowards function as intended though.

    public class follow : MonoBehaviour {
    
        public Transform Target;//your cube
        public float Distance = 10f;//distance for camera
        public float Rate = 0.1f;//rate of follow
    
    	// Use this for initialization
    	void Start () {
    		
    	}
    	
    	// Update is called once per frame
    	void Update () {
    
            Vector3 norm = -this.Target.forward.normalized;
            Vector3 targetCamPos = this.Target.position + (norm * this.Distance);
    
            this.transform.position = Vector3.MoveTowards(this.transform.position, targetCamPos, this.Rate);
    	}
    }


    image

Sign In or Register to comment.