Permanently edit a TextMeshPro source file?

For my current project I created a new TextMeshProUGUI extension class that does some extra stuff in regards to resizing itself and calculating some text size-related things.

Could have made it a second script that attached to the same GameObject as the TMProUGUI object, didn’t want to. Wanted a single object, so I extended TextMeshProUGUI

Unnecessary Explanation as to why/what I changed. Was about to delete it, thought I’d leave it in case anyone cares.

I have two places where I needed ‘deeper’ access to TMPro.

I wanted to override the .text property so that when text was set, I could act on that. Could have made my own property, but I wanted this to be a drop-in replacement for TextMeshProUGUI. If I had to make my own new property, lots of other code that updates TextMeshProUGUI.text would have needed to be changed. The property in TMP_Text.cs is “public string text” - I need it to be “public virtual string text”.

Second, I do some custom calculations related to the auto sizing, and that requires me to manually reset
m_recursiveCount, which is set as private, with no public/virtual methods to reset it’s value. So I need that to be protected int m_recursiveCount instead of private.

TL/DR for those who didn’t care to read the unnecessary spoiler text above: I needed to tweak stuff in TMP_Text.cs for my inherited class to work.

So I went in and updated the TMP_Text.cs file and made those changes. And everything works great. However, every time close and restart unity, the TMP_Text.cs file is “restored”, overwriting the changes I’ve made, and I need to re-edit it again.

How can I edit that file so that the changes stay? Either for just this project, or for any project I work on from this computer. They don’t break anything for future software I write (where I may forget about those changes) - it just makes those two properties/variables accessible to classes that inherit from TMP_Text.

I personally would make a “copy” of the source and have my own version that I use. TMP_TextUnbridledGames or whatever.

Otherwise there isnt a way around this that I know of, that wont cut you off from future updates.

2 Likes

Ideally Unity would provide a GitHub (or similar) public repository for TMPro. They already provide the source code for free in the package manager version. If there was a public repo it would be so much easier to fork it, make your own changes, and still be able to merge in updates from the main repo relatively painlessly.

1 Like

So it that why it’s updating itself? Every startup it’s pulling anything it needs (that I changed?) from the repo?

If thats the case looks like I need to write a script that, upon Unity booting up, re-overwrites the newly overwritten TMP_Text.cs file with my version.

Thats a really silly solution to a pretty simple to solve problem. I have the source code right in front of me. Not sure why I can’t fork the repo and still have access to updates. Lame. :slight_smile:

Whenever you start the project, Unity will make sure any packages match the expected contents of the packages. That means deleting anything you’ve added and reverting any changes you’ve made to the package. You’ll probably be better off making TMP a “local” package, and then editing it. It’s not as convenient as branching the source code, since you’ll cut yourself off from future updates, but it’s much more straightforward than trying to create an editor script to edit source code files at startup…

In your manifest.json file, you can point to a local directory to contain the package contents. For example:

    "com.unity.render-pipelines.high-definition": "file:../LocalPackages/com.unity.render-pipelines.high-definition@7.5.1",

You’d then just make a copy of the source directory and place it at the location you’ve specified. Now Unity won’t touch that code, and you’re free to edit it without fear that Unity will automatically overwrite your changes.

The caveats are:

  • For large projects, it may be hugely inconvenient to have to check in a massive Local copy of a package just to make a small change.
  • You no longer get any update to the package, and if you want to upgrade to a new version, you’ll need to reapply your manual changes to the package.

For this reason, you might want to version control “ignore” the local package, and instead create patch files to apply your changes.

1 Like

Yes, that’s a silly solution indeed. I would not allow that on a project I was involved in.

The normal human approach is to make a copy of the class, rename it, make your changes and use that one from now on.

An even better approach is to subclass the target class and make your changes fo the subclass, if possible.

But if you insist on reaching for duct tape and bailing wire solutions such as pre-copying your changes in, that’s entirely on you, as it is your project.

2 Likes

Can’t just make a copy of the class and rename it. Well not without days or weeks of completely rebuilding the project from the ground up.

I DID subclass the target class. I can’t make the changes in the subclass, thats the entire point of my post.

But thanks for the Stack Overflow’esque comment insulting the “it’s dumb I’m being forced to do this” method while also at the same time mentioning that blatantly obvious fact that I CAN do it however I want as it’s my project. But I guess thanks for letting me know that you wouldn’t put up with that kinda malarkey on any project that YOU are involved in. Now it’s clear to anyone who reads this that you are better than me. Achievement Unlocked.

Anyway. Thanks to those who helped point me in the right direction for a workable solution to my problem. Just don’t try pulling any of that tomfoolery on any of Kurt’s projects.

EDIT: This post was a reply to apparently-now-removed posts by someone ridiculing my approach. Leaving this here, because as the original posts I replied to seem to have been removed (or the user blocked me so I can no longer see them), others have replied to it and I wanted to leave some context in place.

We’re all here to discuss, learn from and teach each other. There’s never a need to vomit garbage all over someone’s attempts to learn.

“Thats a really bad idea, and here’s why” is a HELL of a lot different than a judgmental and condescending “You can duct tape hack it like that if you want, but I’d never allow that kind of garbage in any of MY projects.” The first possibly teaches someone something they may need to know. The second just discourages them and makes you look like an ahole.

I apologize for coming off rough on that, but I am extremely against solutions that incur unspecified future technical debt that is completely out of sight, such as a copy-over-step.

If I hack a TMPro class to add a feature, it would be in mainline code and if a future update to TMPro breaks my hack, I expect that. I would have source control history to prove what I changed between the stock file and the copy I made above. I would also name the class clearly (see below) so I knew where to look in the source control files.

You can write an editor script to search all scenes, prefabs and assets for one and switch to the other. There’s examples of such scripts already out there. Assuming the property names don’t change, it would all remain connected.

Personally I would just use a text editor and find-in-files to search/replace the Unity guid number from the original class to your new hacked class guid. Pull up the .meta for the classes involved and you can trivially cut them over, from the red dot GUID to the green dot GUID in the image below:

6460063--724315--guids.png

Files you want would be .unity, .prefab and .asset, but you can grep your project to make sure there’s no other uses. With source control the risk is minimal and I’ve never had it go wrong.

2 Likes

He didn’t “hack” the class to add in a feature, he only changed the access (private to protected and added a virtual keyword) so that his own sub class, where he implements the new features, actually works. Special cases like that is one of the reasons why they include the original source files, so that you can edit them if needed and it seems to be a valid reason to edit the orginal file in this case, so don’t worry OP.

The point though is everytime it gets overwritten. So you still need to make a copy, not just edit the original. Unless you have some magic way around that which everyone is not aware of?

No-one has said dont make the edit, just we are trying to help OP not keep losing said edit all the time. Editing the original will lock you out of updates or mean you have extra work everytime an update comes in from TMP.

First off, thanks for the support folks. The “you’re doing it wrong you simple minded idiot!” mindset on technology/coding related forums is rampant. I stopped asking questions on Stack Overflow years ago because I got tired of being berated. Side note - while googling yesterday, I saw a question there about something unity related, and it was tagged with Unity.

Someone ACTUALLY bothered to comment on it: “The unity tag is for Windows Unity. Please stop abusing the tag.”

Seriously. ABUSING the tag. First off, what in the hell is windows unity? Googling it gives me an entire page full of Unity links, and some “Unity Windows” energy efficient windows page.

Anyway - sorry - seeing stuff like that makes my blood boil. It’s not a club you need to earn your way into by enduring hazing and ridicule. So I sort of dig my heels in when I see it happen.

Back to the topic at hand: The solution I came up with that so far seems to be working, AND required the least amount of hacking around/effort, was to edit the TMP_Text.cs file thats located in the local package cache - its where Unity keeps a local copy of git repos so that files can be updated/accessed even when offline. In windows it’s in Application Data\Local\Unity\cache

Edited the file there, restarted unity, and my changes propagated into the project’s package.

Is it the best solution? Certainly not. Will it break if I upgrade TextMeshPro? Absolutey. Did it take more than 2 minutes to implement and for the most part solves the problem? Yes it did. Thats a win in my book.

I’m not exactly surprised to find that approach worked, but I’d still personally be reluctant to use it. I just wouldn’t trust that the contents of Unity’s package cache wouldn’t get overwritten at some point. Perhaps it isn’t happening right now, but I would be cautious about assuming that content is stable. For all we know, Unity might refresh that, delete it, or change something about its cache approach that breaks your implementation. And the perhaps bigger concern is that if something does break about it, it might not be at all obvious. Not to mention your changes aren’t version controlled. (Maybe not a big deal if you’re literally changing a single file, but even so, I’d want some kind of backup.)

Anyway, compared to your approach, using the “local” package approach I mentioned above is almost identical, and a fair bit safer. Just copy the package cache directory to some other location that your Unity project can access, then update your manifest.json to point to that directory. That probably still falls within your 2 minute benchmark, and it’s stable. You don’t have to wonder if that local content is going to be clobbered by something. Then it’s up to you whether to check that package into your version control, or ignore it. Anyway, I would definitely recommend that approach over manually editing the cache files directly in AppData.

Thanks for that. I’ll give it a shot. I looked into it, and couldn’t figure out what you meant in your original post about maybe wanting to version control “ignore” it, and while poking around in there is when I discovered the local cache, tried editing that file, saw it worked and thought “good enough.”

I’ll try what you suggested though - I’d rather not have to deal with editing those files AGAIN. It really is just adding a “virtual” and changing a single “private” to “protected” in TMP_Text.cs, so it’s not like the changes are extensive and complicated, but I’d really rather not have to mess with this ever again. At least in this project.

I really wish this were an easier change. The component I made adds a lot of cool features to TextMeshProUGUI for those who have an extensive UI. The texts recttransform automatically updating its size based on changes in text, auto font sizing, and minimum/maximum size thresholds. A big reason why I wanted this was I had an area with text that could grow, and auto-font-sizing was great, until the text got so long it really needed to wrap to a newline, by which point the text was real small. Standard TMP won’t auto-wrap text until it reaches the minimum size. So set the minimum size large enough, you say? Well what if, after reaching the minimum size, it wraps to a new line, and your line now looks like:

This is my really long line of text which has shrunk and wrapped to a new
line.

Well thats just dumb. Text is smaller than it needs to be, and a whole line is wasted on one word. What if there was a "if the text gets smaller than X, then stop shrinking it, and wrap it to a new line. Now that there’s a second line available, let’s bring the size back up so it’s not so small. And lets make sure we also make the RectTransform taller so any LayoutElements around it (If i’m in a Vertical Layout Group) can adjust themselves accordingly.

Now you have a much more readable:

This is my really long line of text which has
shrunk and wrapped to a new line.

Anyway. Thats what I worked on. Its just TextMeshProUGUI with a bunch of stuff added on that met my needs. And I’d love to put it up on github or the asset store, but if it requires manually changing code inside the base TMP - thats a dealbreaker for most people.

I could get around changing the “text” property - thats not strictly necessary, but is nice if you’re using it as a drop in replacement for the base component. But the m_WhateverRecurseCounter that the text autosizing code uses is pretty mandatory that the script accesses that, as code I wrote mimics the autoresizing functions in TMPro, but also calls base methods in the parent class that rely on that private variable.

I tried copying down and re-implementing the autosizing code but every method I pulled down referenced even more private methods/properties. After a few steps in I gave up and just changed that variable to protected.

Sorry for the long winded post. Not sure if any of this is helpful or relevant. I’m working in a hotel room this week and I havent seen another human soul in 3 days and, well… conversation keeps me sane :slight_smile:

You might try sharing your code changes over on the TMP sub-forum. Maybe Stephan would consider adding it as a feature in general.

As far as what I meant by “ignoring” the local directory, I’m referring to a concept in version control systems where you tell the system not to track changes to specific files. Unity project tend to contain a lot of included or generated files, some of which are very large, and there’s really no value to keeping track of the version history of all of those files, so it’s typical to tell the version control system to “ignore” those files.

Creating a local copy of a package, and then editing it, is the recommended approach by Unity (AFAIK), but I personally don’t feel like adding hundreds of megabytes of files to my version control system just to track a small change to a package. So, for me, my typical process is to copy the package to a local directory that I have configured the version control system to “ignore”. Now those hundreds of MB won’t be added to my git repo. But, now I’m not tracking any of the changes I’ve made to those files. My solution, in this case, is to generate what’s known as a “patch” file, which is similar to your original plan of creating some way to convert the original code to the modified version of the code you want. Patch files contain basically just the changes you’ve made, not the full files, so they’re quite small. I usually just create patch files, and keep those patch files in my version control system. That way, I have a record of the changes, and can easily reapply them in the future if I decide to update to a new version of the package.

Aah ok. I thought you mean to somehow tell UNITY to Ignore updates to the TMPro package, not for my own personal version control to ignore it. That makes a lot more sense now. I did try at least a few google searches for “unity version control ignore package” to no avail, before I quit and just edited the package cache.

Thanks for clearing that up. Nothing like spending time trying to find the answer to a question by totally misunderstanding the problem in the first place :slight_smile:

That’s probably a good description of 50% of my software problems.

R/ProgrammerHumor on Reddit keeps me sane. Every weird or obscure meme that makes me laugh out loud reassures me that I’m not the only idiot who spent 3 hours debugging the wrong file because I forgot about the thing I commented out in the other file.