Posts

LG 32MN60T as a retro-gaming CRT replacement (using OSSC for upscaling)

 



My modest proposal for a CRT replacement: a fast + large LCD monitor with an OSSC to handle conversion from component to HDMI.  I still have a very good condition consumer Sony TV (27FS100) to compare to, so I can judge how well this works even if I can't photograph it successfully.

I'm using a 32" LG 32MN60T, which is big enough for playing from a couch. Because it's a computer monitor, it's super fast by TV standards (2.5ms input lag, but 8ms response time). It's only 1080p, but given what I'm going to send it from the OSSC, that's FINE.

The OSSC is set up to take 480i input and convert it to 960p using 2x BOB deinterlacing. In a somewhat feeble attempt to emulate the rapid phosphor decay of an interlaced screen, I selected the mode where rather than true line doubling it draws the current and previous frame simultaneously, but the old frame at 25% of original brightness. Think of it as 25% LACE,75% BOB. This nicely masks a lot of the BOB artifacts (flicker), and likewise the LACE artifacts (horizontal lines when the screen moves).

The result? not quite CRT quality, but before I had finished playing a single level of Ratchet and Clank I had already forgotten that I wasn't playing on a CRT. Not that I'm getting rid of my Sony CRT for now (well, make me a $$$ offer, purist) but this is a viable way forward. And FYI, I'm picky. This is the first time I felt like I had really seen a viable option that wasn't super expensive. 

You might quibble with calling this screen "fast" because of the response time. But it's an IPS which I consider mandatory if you are going to play from the couch. And the slow temporal response time between frames, in effect, duplicates some of the spatial blurring within a frame that a TV's CRT has. So pixels were pleasantly blended, ideal for old 3D games from the PS2 era. I think it would look quite good for a 2D game but didn't try that.

LG 32MN60T input lag and response time measured with the pi lag tester pro

 2021 model IPS LCD/LED monitor, 1080p at up to 75hz. Actually refreshes at 75hz if that's the signal you give it.  

Input lag at screen's top was 2.5ms. Response Time was another 8ms on top of that. The screen has several "modes" including game mode, none of which change anything. It also has a Response Time option, with 3 levels of speed. Increasing the response speed increased the brightness overshot in a black to white transition, but didn't really make the slope of the transition any faster, so the response time stayed about the same: 8ms. Perhaps other transitions fare better, but I can see no reason to use anything other than the normal "response time" setting, as that overshot surely leads to visual artifacts (I didn't test real input to see how much of an effect that was).

VESA mountable, good viewing angles.  Enormously too large for 1080p on your desktop. This is meant for more of a living room setting where you are sitting further away (coop gaming?) but why not just use a good TV instead? Perhaps because 2.5ms lag is hard to match without going very high end in the TV space.



ViewSonic VA2759-smh input lag and response time measured with the piLagTester PRO

I tested a used ViewSonic VA2759, made in 2016. Only seems to support 75hz refresh rates and below (but does scan out at 75hz).

It offers a "game" mode, but this only changes the color profile, all timing is identical across all "modes".

It does offer an option to change response time, from "standard" to "ultrafast". This did change response time, but not input lag. 

Input lag in the upper corner is 2.5ms. Response time in standard mode is an additional 10ms. Switching to Ultrafast it drops to 7ms. The luminance curves over time show no ugly artifacts in "ultrafast", at least in the black to white transition. 

It accepts interlaced video, which is displayed using BOB deinterlacing. 

Colors looked fine, but viewing angles seemed a tad narrow for an IPS.  

Aiming the camera with both the mouse and Joystick simultaneously in Unity using legacy InputManager

Many Unity games just support mouse and keyboard (WASD) for first person control. But if you are using the legacy input manager (most if not all the free first person controllers do) it's easy to make the gamepad right joystick work for to aim the camera and still support mouse look too!

The secret is the name of an axis (such as mouse y) can refer to multiple input devices. Unity will respond to whichever is being moved. So you just need to add a definition in the input manager for Mouse Y & X that also refers to the joystick. You can use the GUI for this (Edit > Project Settings > Input Manager), or you can edit the file directly, which is what I describe here.

Right click on The Assets folder in the Project tab and select Show in explorer. From here open the ProjectSettings folder and then edit InputManager.asset using your favorite text editor. 

Go to the bottom of the file and add a new line just above the last line, which should be  

 m_UsePhysicalKeys: 0 

then paste this code into that empty line:


  - serializedVersion: 3
    m_Name: Mouse X
    descriptiveName: R_Joy_x
    descriptiveNegativeName: 
    negativeButton: 
    positiveButton: 
    altNegativeButton: 
    altPositiveButton: 
    gravity: 0
    dead: 0.1
    sensitivity: 1
    snap: 0
    invert: 0
    type: 2
    axis: 3
    joyNum: 0
  - serializedVersion: 3
    m_Name: Mouse Y
    descriptiveName: R_Joy_y
    descriptiveNegativeName: 
    negativeButton: 
    positiveButton: 
    altNegativeButton: 
    altPositiveButton: 
    gravity: 0
    dead: 0.1
    sensitivity: 1
    snap: 0
    invert: 0
    type: 2
    axis: 4
    joyNum: 0

Note the spaces matter. the - has to be on the same column as the m in m_UsePhysicalKeys

Tested and working in Unity 2021.2.33f1 (latest LTS version as of 2023).

Unity first person player controllers compared

This is a roundup of Unity C# first person controllers. A controller is the code that makes your player move based on keyboard/gamepad/mouse input. For now, only free options are included.

Modular First Person Controller 

This is a good starting place for your own controller, as everything is simple and well labeled in a single script file. It will need some work, however, as the actual control has problems.

Horizontal movement has a major issue: it's easy to get stuck on a wall just by walking at it on a 45 degree angle (this also happens when jumping, meaning you can cling to walls). Jumping has major issues too - if you push against a wall and try to jump it won't let you. Jumping when moving downhill isn't possible, either. Steps are hard, but not impossible; the step size just has to be very short, and this is not tunable. It uses a Rigidbody to do most of the work, so the problems can probably be fixed by changing how that is configured.

 Here's the jumping code:

  // Gets input and calls jump method && isGrounded
        if(enableJump && Input.GetKeyDown(jumpKey) && isGrounded)
        {
            Jump();
        }


 // Sets isGrounded based on a raycast sent straight down from the player object
    private void CheckGround()
    {
        Vector3 origin = new Vector3(transform.position.xtransform.position.y - (transform.localScale.y * .5f), transform.position.z);
        Vector3 direction = transform.TransformDirection(Vector3.down);
        float distance = .75f;

        if (Physics.Raycast(origindirectionout RaycastHit hitdistance))
        {
            Debug.DrawRay(origindirection * distanceColor.red);
            isGrounded = true;
        }
        else
             isGrounded = false;
     }

you can help (but not fix completely) the issue with not being able to jump going downhill by increasing the distance value, as I have already done above. 

private void Jump()
    {
        // Adds force to the player rigidbody to jump
        if (isGrounded)
        {
            rb.AddForce(0fjumpPower0fForceMode.Impulse);
            isGrounded = false;
        }
        // When crouched and using toggle system, will uncrouch for a jump
        if(isCrouched && !holdToCrouch)
        {
            Crouch();
        }
    }

It uses the legacy InputManager meaning only mouse+keyboard support out of the box, but this is fixable. 

First Person V2.1.0 (new project from built-in template)

You get this from the Unity Hub instead of the asset store. It uses the new input system, and as such is much more complex than the previous example. But with that complexity comes controller + mouse + keyboard support. Movement is smooth, with no catching on walls, and no more problems with jumping going down hill. No issues with walking into the ceiling pushing you thru the floor, either. Unfortunately when a jump hits a ceiling it behaves quite weird - either you get stuck against it for a couple frames and then drop normally, or you slowly slide along them and then drop. 

The mouse + keyboard support feels well tuned, but the gamepad input is super twitchy. The new input system spreads things out over a lot of different files and UIs, so it's far from obvious how to fix this, but I did find the following hack that works ok: change line 138 of FirstPersonController.cs to 

float deltaTimeMultiplier = IsCurrentDeviceMouse ? 1.0f : Time.deltaTime/3;

 Also, movement in the air is very floaty (basically the same as moving on the ground) which makes any kind of platforming very hard. The code for the controller is very short, but doesn't really explain its logic, eg. here's the jump code:

   private void JumpAndGravity()
        {
            if (Grounded)
            {
                // reset the fall timeout timer
                _fallTimeoutDelta = FallTimeout;

                // stop our velocity dropping infinitely when grounded
                if (_verticalVelocity < 0.0f)
                     _verticalVelocity = -2f;
                // Jump
                if (_input.jump && _jumpTimeoutDelta <= 0.0f)
                    // the square root of H * -2 * G = how much velocity needed to reach desired height
                    _verticalVelocity = Mathf.Sqrt(JumpHeight * -2f * Gravity);
      
                // jump timeout
                if (_jumpTimeoutDelta >= 0.0f)
                    _jumpTimeoutDelta -= Time.deltaTime;     
            }
            else
            {
                // reset the jump timeout timer
                _jumpTimeoutDelta = JumpTimeout;
                // fall timeout
                if (_fallTimeoutDelta >= 0.0f)
                   _fallTimeoutDelta -= Time.deltaTime;
     
                // if we are not grounded, do not jump
                _input.jump = false;
            }
            // apply gravity over time if under terminal (multiply by delta time twice to linearly speed up over time)
            if (_verticalVelocity < _terminalVelocity)
                _verticalVelocity += Gravity * Time.deltaTime;
        }


Starter Assets - First Person Character Controller V1.2

Seems to be nearly identical to the controller you get from the built-in templet described above, despite the very different version number (1.2 vs 2.1).


Mini First Person Controller V1.2.0

Maybe useful as a starting point, but too broken for anything beyond that. 

Movement is nice and smooth. If you walk into a wall you glide along it as you would expect, not getting stuck, and you can't slip thru narrow gaps. You can glide up steps if they are not too steep, which seems to come from the fact that a capsule collider is used, which means that it's not particularly tunable. Also, if you stand close enough to an edge (so that your capsule's lowest point is over open space) you start to drift over the edge. At least when standing squarely on a slope there's no problem with slipping down, and you can jump when walking down a slope.

If you jump while moving toward a wall you get stuck on it, almost like wall jumping. Indeed, if you jump into the air and then push against a wall you stick to it, slowly drifting down. That could be a fun mechanic for some games if it were optional. Worse, however, pushing against any kind of surface, be it a wall or an overly high step, prevents you from jumping at all. 

At least jumping and hitting your head works as expected - you immediately bounce back downwards. With so much broken (and with most of the behavior stemming from using a rigid body) I'm not sure it's worth showing the fairly minimal source. Here's a couple of very familiar lines, though:

  bool isGroundedNow = Physics.Raycast(RaycastOrigin, Vector3.down, distanceThreshold * 2);


    // Jump when the Jump button is pressed and we are on the ground.
        if (Input.GetButtonDown("Jump") && (!groundCheck || groundCheck.isGrounded))
        {
            rigidbody.AddForce(Vector3.up * 100 * jumpStrength);
            Jumped?.Invoke();
        }

It uses the legacy InputManager meaning only mouse+keyboard support out of the box, but this is fixable. 


Simple FPS Controller v1.4

The name is misleading; there's no support for shooting/aiming/etc. Aside from a grappling hook, this is just a standard first person controller that also features wall running. While the code is nice and simple making it potentially useful as a starting place, it has major issues. Moving toward a wall while in the air causes you to stick to it indefinitely, so unless you desire that mechanic, this is not for you. And to be clear, that's not how wall running works, that's just how the rigidbody behaves for all vertical surfaces.

 At least you can still jump when moving toward a wall. And you don't slide down on slopes when standing still. 

Here's how jumping works:

        if (isGrounded)
        {
            // Jump
            if (Input.GetButton("Jump") && !jumpBlocked)
            {
                rb.AddForce(-jumpForce * rb.mass * Vector3.down);
                jumpBlocked = true;
                Invoke("UnblockJump", jumpCooldown);
            }
            // Ground controller
            rb.velocity = Vector3.Lerp(rb.velocity, inputForce, changeInStageSpeed * Time.fixedDeltaTime);
        }
        else
            // Air control
            rb.velocity = ClampSqrMag(rb.velocity + inputForce * Time.fixedDeltaTime, rb.velocity.sqrMagnitude);

Advanced First-Person Controller

Maybe useful as a starting point, but too broken for anything beyond that.

 The major thing this brings is a HUD (health bar, etc) and camera shake when you land. Neither are great; the HUD often clips into geometry and half-disappears. The camera shake is really neat and effective AT FIRST, and then you'll be shouting how do I turn this effect off?!

Movement is fairly nice and smooth. If you walk into a wall you glide along it as you would expect, not getting stuck, and you can't slip thru narrow gaps. Occasionally though, you bounce off a little, which feels like a glitch.  You can glide up steps if they are not too steep, which seems to come from the fact that a capsule collider is used, which means that it's not particularly tunable. Unfortunately, rather than climbing up a step, it's more like a little "jump" and thus you usually get the camera shake effect after each step. Also, if you stand close enough to an edge (so that your capsule's lowest point is over open space) you start to drift over the edge.

Slopes are problematic. You start to drift downwards whenever standing on slope of any magnitude.  If it's moderately steep and you start walking down you actually move forward a little and then drop, causing another camera shake. At least you can jump while walking down a slope.

Hitting your head mostly works as expected - you do get pushed back a little if it's sloped at all, but the effect is mild and somewhat realistic (assuming your head is made of rubber). Most of the bounce is directed down. And if you walk into a sloped ceiling it doesn't push you thru the floor.

Much of this behavior stems from the use of a rigidbody, but the code still manages to be complex, and not particularly well layed out or commented.

        public virtual void Accelerate () {
            LookingForGround ();
            MoveTorwardsAcceleration ();
            if (!isMovementAvailable) return;
            if (!rb) return;
            if (System.Math.Abs(movementInputValues.x) < epsilon & System.Math.Abs(movementInputValues.y) < epsilon) return;
            if (!isAirControl) {
                if (!isGrounded) return;
            }
            if (rb.velocity.magnitude > 1.0f) {
                rb.interpolation = RigidbodyInterpolation.Extrapolate;
            }
            else {
                rb.interpolation = RigidbodyInterpolation.Interpolate;
            }
            delta = new Vector3 (movementInputValues.x, 0, movementInputValues.y);
            delta = Vector3.ClampMagnitude (delta, 1);
            delta = rb.transform.TransformDirection (delta) * currentAcceleration;
            vector3_Target = new Vector3 (delta.x, rb.velocity.y, delta.z);
            rb.velocity = Vector3.SmoothDamp (rb.velocity, vector3_Target, ref vector3_Reference, Time.smoothDeltaTime * movementSmoothing);
        }


It uses the legacy InputManager meaning only mouse+keyboard support out of the box, but this is fixable. 

The C Ternary Operator ?: is for (lisp) lovers

 The C/C++/JAVA Ternary Operator (or inline if as I like to call it): 

(test) ? do-if-true : do-if-false 

as in:

printf("x is %s than y", (x > y) ? "larger" : "smaller");

Also legal is to place the parentheses around the whole thing:

(test ? if-true : if-false)

All the hits on google start with half a page of blather before they get to a complete example like the one above. Which is probably how they get traffic, but what a waste. I'm putting the blather here where it's easy to skip!

I love this operator because of that short LISP detour I took in my 20s (after the Smalltalk detour of my teens, and before my main path of being a Matlab hacker in my 30s). Like an s-expression it allows you to do things that would otherwise require temporary variables, or lots of duplicate code. People criticize the ternary operator because it's confusing, which is fair. So is Lisp if you don't use it often (and goodness knows I haven't written Lisp in more than a decade so I can really sympathize with that one).

And yes there's a bug in my example. Consider x = y = 1; 

You could do this, however:

(x > y ? "larger" : (x == y ? "equal" : "smaller")) 

but I have to admit at that point I start to agree with the detractors that it's too hard to read. On the other hand, the equivalent if statement either has 3 printfs or a temporary variable, which isn't that great either. 

Bonus chatter:

lisp syntax (from memory, untested):

(if (> x y) "larger" (if (= y x) "equal" "smaller" ))

and the equivalent if-based solution in c:

if (x > y)
  printf("x is larger than y");
else if (x == y)
  printf("x equals y");
else
  printf("x is smaller than y");


Fake TI ADS1115 ADC sold as ADS1015 - BOGI vs BRPI marking

These days it's getting almost impossible to get a functioning ADS1015, a great high-speed ADC (analog to digital converter) made by TI. AdaFruit makes a very nice board featuring it that I've used in a lot of projects. Or rather, I've used their board design, but ordered it from Chinese manufactures on aliexpress and ebay, who used the open source schematics to make much cheaper boards. For the first 20-30 I purchased there was no problem, but lately all the boards that are supposed to be ADS1015s have actually had the ADS1115. This might seem like an upgrade, because the ADS 1015 only has 12 bit resolution, and the ADS1115 offers 16 bits. But higher resolution has to come at a cost, and in this case the cost is speed - the ADS1115 maxes out at 860 samples per second, whereas the 1015 can take measurements 3300 times a second - almost 4 times faster. 

Since the 1015 and 1115 and electrically compatible, the same board design works with both, meaning that you can't tell at a glance which you have. The chip has microscopic markings on it that identify which version it is; reading the datasheets around page 40 you can determine that the ADS 1115 is marked BOGI, and the ADS1015 is marked BRPI.


I've seen several boards marked "12-bit ADS1015" that actually had the BOGI chip on it. Sadly, more recently, I've even seen a chip marked BRPI that turned out to be a ADS1115 - or more likely a cheap clone of the ADS1115 that is not only slow like the 1115, but doesn't really have the 16 bit resolution either. Worst of both worlds. 

So to conclude: check the chip, if it's marked BOGI it's definitely not a ADS1015. And if it's marked BRPI you may have a real ADS 1015, but you'll need to check that it can actually support the full 3300hz sampling rate by connecting to an i2c bus and setting it to the max sample rate. 





Email me

Name

Email *

Message *