Forum rules - please read before posting.

Cursor scaling

Hi there,

I can't get the cursor to scale correctly for different screen ratios. I set all my menus to scale only according to height, whereas the cursor seem to react to the width. It would be great if the Cursor manager allowed us to specify how it cursors will be scaled (width/height - something similar to the Match setting on a Canvas Scaler).

You can see an example in the same repro I posted here:
https://adventurecreator.org/forum/discussion/13729/hotspot-menu-fading-out-over-the-wrong-hotspot

  1. Play Scene1 and resize the Game window using Free Aspect
  2. Issue 1: Notice that if you make the window extremely wide, you get a huge cursor over a tiny game
  3. Issue 2: Notice that the cursor keep scaling outside the aspect ratio entered in the settings manager

Thanks

Comments

  • edited May 2023

    Check out this thread for my solution: https://www.adventurecreator.org/forum/discussion/12780/item-count-alignment#latest

    Chris explained that "the Software cursor size is tied with the screen's width, but you can have it be a fixed size at all times with the Hardware cursor option." For various reasons, I needed to keep the software cursor though, so I went for a custom solution.

    I was originally dealing with a different issue (offsetting the item count correctly at all resolutions), but to fix it I also had to find a solution for the uneven resizing of the software cursor. The first script ("ResizeCursor") is for resizing, and the second for the offset. It works by calculating the correct ratio and updating the cursor size in the AC manager.

    Can you read C#? This script is fairly easy to understand, but you do need to modify it slightly to get the settings you want. I wrote it for the ones I wanted.

  • Hey @Rairun! And thanks for your reply!

    Inspired by your code I tried this:

    float lastCursorRatioX = 0;
    float lastCursorRatioY = 0;
    
    void Update()
    {
        float cursorRatioX = KickStarter.mainCamera.GetPlayableScreenArea(false).size.x / ACScreen.width;
        float cursorRatioY = KickStarter.mainCamera.GetPlayableScreenArea(false).size.y / ACScreen.height;
    
        if (cursorRatioX != lastCursorRatioX || cursorRatioY != lastCursorRatioY)
        {
            var min = Math.Min(cursorRatioX, cursorRatioY);
            //Debug.Log($"cursorRatioX:{cursorRatioX} cursorRatioY:{cursorRatioY} min:{min}");
    
            var newSize = 0.03f * min;
            KickStarter.cursorManager.pointerIcon.size =
                KickStarter.cursorManager.mouseOverIcon.size =
                KickStarter.cursorManager.inventoryCursorSize =
                KickStarter.cursorManager.waitIcon.size = newSize;
    
            for (int i = 0; i < KickStarter.cursorManager.cursorIcons.Count; i++)
                KickStarter.cursorManager.cursorIcons[i].size = newSize;
    
            lastCursorRatioX = cursorRatioX;
            lastCursorRatioY = cursorRatioY;
        }
    }
    

    However, it the cursor still grows as the screen is getting wider (within aspect ratio range specified in Settings)...

    While an interesting idea, I feel this is working against an implementation that arguably would make more sense working against Height rather than Width. Ultimately, I think it would be a really clean solution if users of AC could specify the width/height ratio for the cursor scaling somewhere.

  • edited May 2023

    I'm not sure I understand why you're taking height into account here. As Chris said in the other thread, it has no influence over the size of the cursor (only width does), no matter how tall you make the screen. A crazy proportion like 1280x4000 will give you huge black bars at the bottom and top, but the size of the cursor will still look correct because there are no side bars. Your script isn't working because you're using a y ratio when you shouldn't be doing that.

    All my script is doing is say, "The cursor is getting bigger than desired because it's taking the entire width of the screen into account, including the side bars; let's calculate the ratio between playable width and total width, and adjust the size of the cursor by that."

  • edited May 2023

    If I understand your script correctly, if the screen's aspect ratio perfectly fits the ratio you specified, the cursor will be size 0.03. If the height is greater than expected (giving you black top/bottom bars) it will reduce the size of the cursor when it shouldn't be doing that. If the width is greater than expected (giving you black side bars), then it should work the same as mine. Is it not doing that? It's been working perfectly for me for many months now.

  • edited May 2023

    Now, I agree that it'd be nice if AC set the cursor size based on playable width instead of total width natively! Then we wouldn't need a custom script at all. But Chris didn't seem to think it was a good idea (and he does know way more about the possible implications of this change than I do), so I settled for a custom script. It's been working!

  • he does know way more about the possible implications of this change than I do

    I wouldn't be so sure.

    The Software cursor should be scaled by the width - but this ought to be the playable width rather than the physical width. Perhaps I misused the word "screen" earlier.

    I will look into this.

  • If the width is greater than expected (giving you black side bars), then it should work the same as mine. Is it not doing that? It's been working perfectly for me for many months now.

    It is indeed working. The problem (as I see it) is that when the playable area is within the supported screen ratio (so no letterboxing or pillarboxing is applied), making the playable area wider (but still inside the supported ratio) is still scaling up the cursor.

    I understand this is by design, as AC scales the cursor by width rather than height.

    The Software cursor should be scaled by the width - but this ought to be the playable width rather than the physical width. Perhaps I misused the word "screen" earlier.

    I think that is a good point - there's really no need to ever take the letter/pillar-boxing space into account.

    Maybe I'm looking at this from the wrong direction. I'm used to having all my menus scaling by height, this looks great and consistent when playing within all the supported screen ratios. After all, why should something like a Hotspot menu get bigger just because the screen gets wider? And in my case, that Hotspot menu actually follows the cursor, so the relationship between the cursor and the Hotspot menu currently looks different on 16:9 vs 16:10.

    I'm really not sure what makes more sense - having the cursor (and any menus that follows it) scale by width or by height. In either event, I figured that since it's so easy to specify width/height scaling on a Canvas Scaler, it might be worth it to be able to tell AC to do the same when scaling the cursor :)

  • edited May 2023

    It is indeed working. The problem (as I see it) is that when the playable area is within the supported screen ratio (so no letterboxing or pillarboxing is applied), making the playable area wider (but still inside the supported ratio) is still scaling up the cursor.

    Ah, right, apologies. I get you now. Yes, just a very different use case/art direction from mine! Mine is a pixel art game limited to a single fixed aspect ratio (any other ratio gets pillar or letterboxed), because the gameplay is such that (1) we should not reveal any more scenery if the screen gets wider, and (2) all UI elements, including menus and cursor, must have the exact same size in relation to the game graphics at all times. If your game can support a variety of aspect ratios without revealing secret locations unintentionally etc and breaking some of your puzzles, then it's true, giving the player a wider view (all other things being equal) shouldn't mean increasing the size of the UI, including the cursor.

  • No worries at all, Rairun 👍
    And yes - that's exactly my scenario and why scaling the cursor by height would make perfect sense to me.
  • The Software cursor's size is relative to the screen's width. If you want to keep it fixed, use Hardware.

    If you want total control over the way it reacts to the screen size, use Unity UI, as you can then rely on a Canvas Scaler component that matches that of your Hotspot menu.

  • edited May 2023
    Yes, while using hardware cursor is indeed tempting, Unity's limited cursor size is a bit of a bummer when moving inventory items around.

    Thanks for having a look at the Playable width scaling tho 👍
  • edited May 2023

    Edit: There is an error in the script I provided lol. One sec.

    Edit 2: There you go! This works, I think.

        using System.Collections;
        using System.Collections.Generic;
        using UnityEngine;
        using AC;
        using System;
    
        // This script ensures cursor sizes remain proportional in ALL resolutions.
    
        public class ResizeCursor : MonoBehaviour
        {
            public float baseAspectRatio = 1.78f;
    
            public float pointerIconSize = 0.03f;
            public float mouseOverIconSize = 0.03f;
            public float inventoryCursorSize = 0.04f;
            public float waitIconSize = 0.03f;
            public float interactionIconsSize = 0.03f;
    
            private float pointerIconSizeAdjusted;
            private float mouseOverIconSizeAdjusted;
            private float inventoryCursorSizeAdjusted;
            private float waitIconSizeAdjusted;
            private float interactionIconsSizeAdjusted;
    
            private float lastRatio;
            private double lastAspect;
    
            void Update()
            {
                ResizeCursorToMatchBaseAspectRatio();
                ResizeCursorToPlayableArea();
            }
    
            private void ResizeCursorToMatchBaseAspectRatio()
            {
    
                double currentAspect = Math.Round((KickStarter.mainCamera.GetPlayableScreenArea(false).size.x / KickStarter.mainCamera.GetPlayableScreenArea(false).size.y), 2);
    
                if (currentAspect != lastAspect)
                {
                    float cursorRatio = (float)(baseAspectRatio / currentAspect);
    
                    pointerIconSizeAdjusted = pointerIconSize * cursorRatio;
                    mouseOverIconSizeAdjusted = mouseOverIconSize * cursorRatio;
                    inventoryCursorSizeAdjusted = inventoryCursorSize * cursorRatio;
                    waitIconSizeAdjusted = waitIconSize * cursorRatio;
                    interactionIconsSizeAdjusted = interactionIconsSize * cursorRatio;
    
                    AC.KickStarter.cursorManager.pointerIcon.size = pointerIconSizeAdjusted;            
                    AC.KickStarter.cursorManager.mouseOverIcon.size = mouseOverIconSizeAdjusted;
                    AC.KickStarter.cursorManager.inventoryCursorSize = inventoryCursorSizeAdjusted;
                    AC.KickStarter.cursorManager.waitIcon.size = waitIconSizeAdjusted;
    
                    for (int i = 0; i < KickStarter.cursorManager.cursorIcons.Count; i++)
                    {
                        AC.KickStarter.cursorManager.GetCursorIconFromID(i).size = interactionIconsSizeAdjusted;
                    }
    
                    lastAspect = currentAspect;
                }
            }
    
            private void ResizeCursorToPlayableArea()
            {
                float xScale = (KickStarter.mainCamera) ? KickStarter.mainCamera.GetPlayableScreenArea(false).size.x : ACScreen.width;
                float cursorRatio = xScale / ACScreen.width;
    
                if (cursorRatio != lastRatio)
                {
                    AC.KickStarter.cursorManager.pointerIcon.size = pointerIconSizeAdjusted * cursorRatio;
                    AC.KickStarter.cursorManager.mouseOverIcon.size = mouseOverIconSizeAdjusted * cursorRatio;
                    AC.KickStarter.cursorManager.inventoryCursorSize = inventoryCursorSizeAdjusted * cursorRatio;
                    AC.KickStarter.cursorManager.waitIcon.size = waitIconSizeAdjusted * cursorRatio;
    
                    for (int i = 0; i < KickStarter.cursorManager.cursorIcons.Count; i++)
                    {
                        AC.KickStarter.cursorManager.GetCursorIconFromID(i).size = interactionIconsSizeAdjusted * cursorRatio;                
                    }
    
                    lastRatio = cursorRatio;
                }
            }
        }
    
  • edited May 2023

    Hey, thanks for that @Rairun - works great 👍

    I also tested "Unity UI" for cursor rendering (for some extra flexibility such as being able to animate the cursor). With the canvas scaler set to Height, it works perfectly except for when letterboxing is being applied (but I suspect this will be fixed in the next AC release).

    I think I'm gonna land on using Rairuns script to force software cursor to scale by height, as there doesn't seem to be an easy way to specify individual sizes for each cursor graphic when rendering the cursor with "Unity UI", unless I'm missing something @ChrisIceBox - it always uses the native sizes of the graphics made to fit the RawImage, right?

    Thank you both for your input on this.

  • When using Unity UI, each of the fields you assign in the Unity UI Cursor component are optional.

    If you assign the RawImage, then it'll be set to the selected cursor's texture - but further changes can be made on top of this, such as resizing.

    The easiest way to do this is to rely on animation - since the component also allows you to auto-set an "Icon ID" Animator parameter that'll adjust itself based on the active cursor icon. This in turn could control an animation that modifies the RawImage's size.

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.