Using Engine-Version Specific Code
Making sure code runs in the correct version of Unreal
Why Track the Engine Version
This is more of a pipeline concept that film tends to worry about more, but depending on how the code is handled it is entirely possible that multiple Unreal projects could be relying on the same Python codebase.
When supporting multiple versions of Unreal with the same codebase there is the chance that the same code needs to work in multiple versions of Unreal, even if the Unreal API has changed. This page covers an implementation that will allow us to make Unreal version-specific code, allowing us to simultaneously support multiple engine versions.
How to Get the Engine Version
To see the current Unreal Engine version of an Editor session we can use get_engine_version() :
print(unreal.SystemLibrary.get_engine_version())
# Result:
5.2.1-26001984+++UE5+Release-5.2
For our purposes, we really only need what's before the -
:
5.2.1
The EngineVersion class
I wanted to make a class for this that had minimal dependencies, while it is basic in some regards it does the trick.
I'll start with the complete class and then explain the important parts below:
from functools import total_ordering
import unreal
@total_ordering
class EngineVersion:
"""
Utility class to compare Unreal Versions
Provide the engine version formatted as "5" , "5.0" , "5.0.0"
or leave empty to use the current Unreal Editor session's version
"""
def __init__(self, version: str = ""):
version = str(version)
# Get the current UE session's version if not provided
if not version:
version_info = str(unreal.SystemLibrary.get_engine_version())
version = version_info.split("-", 1)[0]
# Collect the version IDs
version_ids = version.split(".")
if not len(version_ids) or not all([i.isdigit() for i in version_ids]):
raise ValueError(
f"EngineVersion was provided '{version}' - must be in the format of '5' , '5.0' , or '5.0.0'"
)
# Store the version data
version_ids = [int(i) for i in version_ids]
version_ids.extend([0, 0])
self.major, self.minor, self.patch = version_ids[0:3]
@property
def value(self) -> int:
"""The numeric value of the engine version (used for comparison)"""
return self.major*1000000 + self.minor*1000 + self.patch
@property
def version(self) -> str:
"""The string value of the engine version (human-readable)"""
version_string = f"{self.major}.{self.minor}"
if self.patch:
version_string += f".{self.patch}"
return version_string
# The @total_ordering decorator will use __lt__ and __eq__ to make
# the rest of our comparison functions
def __lt__(self, other: 'EngineVersion') -> bool:
if isinstance(other, str) or isinstance(other, float):
other = EngineVersion(other)
if not isinstance(other, EngineVersion):
raise ValueError(f"{other} must be of type EngineVersion!")
return self.value < other.value
def __eq__(self, other) -> bool:
if isinstance(other, str) or isinstance(other, float):
other = EngineVersion(other)
if not isinstance(other, EngineVersion):
raise ValueError(f"{other} must be of type EngineVersion!")
return self.value == other.value
# repr to provide a cleaner debug printout ~ ex: EngineVersion("5.3.0")
def __repr__(self) -> str:
return f"{self.__class__.__name__}(\"{self.version}\")"
The __init__() function
This class supports a few different options to determine the Engine Version:
if Empty, get the current Editor Session's UE version
Strings, such as "5" , "5.0", or "5.0.0"
Numbers, such as
5.2
are also accepted
The value property
This function is what's used to compare itself with others, providing a numeric value that's easier to process
The __lt__() and __eq__() functions
These functions, along with the @total_ordering
decorator on the class, define all of our comparisons. We can compare an EngineVersion class with...
Another EngineVersion object
A number, such as
5.2
A string, such as
"5.2"
Example Uses
Testing this class in 5.2.1
I get the following results:
current_engine_version = EngineVersion()
print(current_engine_version)
print(current_engine_version > EngineVersion(5.2))
print(current_engine_version > 5.3)
print(current_engine_version == "5.2.1")
# Results:
# EngineVersion("5.2.1")
# True
# False
# True
Our code can now safely support newer features that come out:
def force_update_sequencer():
"Force the Sequencer UI to re-evaluate itself"
if EngineVersion() >= 5.5:
# New method added in 5.5 to force update sequencer
LevelSequenceEditorBlueprintLibrary.force_update()
else:
# The old method relies on changing the current frame to force a refresh
current_frame = unreal.LevelSequenceEditorBlueprintLibrary().get_current_time()
unreal.LevelSequenceEditorBlueprintLibrary().set_current_time(current_frame + 1)
unreal.LevelSequenceEditorBlueprintLibrary().set_current_time(current_frame)
We can use the new 5.5 command while still supporting the method used for older engine versions
Last updated