How to get class reference from name variable

Discussions about Coding and Scripting
1337GameDev
Experienced
Posts: 85
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

How to get class reference from name variable

Post by 1337GameDev » Sat Feb 27, 2021 8:53 am

I routinely use / see the following:

Code: Select all

class'ClassNameHere'
to get a reference to a class type.

How would this be made generic?

EG:

Code: Select all

function ChildOf(name className) {
    return classIsChildOf(self.class, class'valueOfClassName');
}
I know about:

Code: Select all

class<Pawn>
In being able to specify a parameter has to be of Pawn/subclass, but not sure of how to specify that via a name type.

Any ideas?

Eternity
Average
Posts: 48
Joined: Sat Nov 30, 2019 10:56 pm

Re: How to get class reference from name variable

Post by Eternity » Sat Feb 27, 2021 4:11 pm

DynamicLoadObject seems to be the only way...

User avatar
Feralidragon
Godlike
Posts: 5300
Joined: Wed Feb 27, 2008 6:24 pm
Personal rank: Work In Progress
Location: Liandri

Re: How to get class reference from name variable

Post by Feralidragon » Sat Feb 27, 2021 7:39 pm

I am not sure what exactly you mean, nor what you mean to do with it...

As far as checking classes goes (if this is your objective), you have IsA:

Code: Select all

if (SomeObject.IsA('ClassName')) {
    ...
}
where you can check whether an object/actor is of the same class or a subclass of the given class given as a name.

Either way, having a concrete use-case (as in your ultimate objective) would help a lot to understand what you want to do, so we can say what options you have.

1337GameDev
Experienced
Posts: 85
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: How to get class reference from name variable

Post by 1337GameDev » Sun Feb 28, 2021 6:34 am

Essentially....

I want to have an Actor, that can be configured on a map, via var() properties, and entries into a 32 index array be added. These names would be class names of an inventory object to check a player for.

I have these methods to check if a player has a given inventory object:

Code: Select all

static function bool HasInventoryObj(Actor actor, class<Inventory> invClass, out Inventory inv, optional bool bNoSubclass) {
    for(inv=actor.inventory; inv!=None; inv=inv.Inventory) {
        if(class'TypeHelper'.Static.classIsRelatedTo(invClass, inv.class, bNoSubclass) != -1) {
            return true;
        }
    }

    inv = None;
    return false;
}

/*
 * Return:
 * -1 - if firstClass is not relationed to secondClass
 * 0 - if firstClass is the same class as secondClass
 * 1 - if firstClass is a child of secondClass
 * 2 - if firstClass is a parent of secondClass
 */
 static function int classIsRelatedTo(class firstClass, class secondClass, optional bool bNoSubClass) {
    //Log(firstClass@secondClass@firstClass == secondClass);
    if(firstClass == secondClass) {
        return 0;
    }

    if(!bNoSubClass) {
        if(classIsChildOf(firstClass, secondClass)) {
            return 1;
        } else if(classIsChildOf(secondClass, firstClass)) {
            return 2;
        }
    }

    return -1;
}
I want to check for a given name of inventory object, and if the player has it, but given the methods normally take a reference dictated by:

class'Class1'

Hmm, ideas?

PS:
Eternity wrote:
Sat Feb 27, 2021 4:11 pm
DynamicLoadObject seems to be the only way...
Isn't this used to instantiate an instance of a given object, by name, not get a class<Class1> style reference?

Hmm... digging further, i see in

Code: Select all

DeathMatchPlus.uc - Line: 1054

Code: Select all

function GiveWeapon(Pawn PlayerPawn, string aClassName )
{
	local class<Weapon> WeaponClass;
	local Weapon NewWeapon;

	WeaponClass = class<Weapon>(DynamicLoadObject(aClassName, class'Class'));

	if( PlayerPawn.FindInventoryType(WeaponClass) != None )
		return;
	newWeapon = Spawn(WeaponClass);
	if( newWeapon != None )
	{
		newWeapon.RespawnTime = 0.0;
		newWeapon.GiveTo(PlayerPawn);
		newWeapon.bHeldItem = true;
		newWeapon.GiveAmmo(PlayerPawn);
		newWeapon.SetSwitchPriority(PlayerPawn);
		newWeapon.WeaponSet(PlayerPawn);
		newWeapon.AmbientGlow = 0;
		if ( PlayerPawn.IsA('PlayerPawn') )
			newWeapon.SetHand(PlayerPawn(PlayerPawn).Handedness);
		else
			newWeapon.GotoState('Idle');
		PlayerPawn.Weapon.GotoState('DownWeapon');
		PlayerPawn.PendingWeapon = None;
		PlayerPawn.Weapon = newWeapon;
	}
}
Hmm, does "DynamicLoadObject" create an instance? It seems not...

User avatar
sektor2111
Godlike
Posts: 5209
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: How to get class reference from name variable

Post by sektor2111 » Sun Feb 28, 2021 7:41 am

DynamicLoad is slow and it loads class somewhere - not in LEVEL. If you you are looking for this inside Level, you need to spawn it - and... only actors are spawned, not objects.
See this... First it's a "dynamicload" - if existence it's possible then Spawn it:

Code: Select all

				LevelName1 = Caps(Right(LevelName1,J))$".StuffTweaker";
				LevelName1 = "P_"$LevelName1;
				log ("Old module and Target class is called >"@LevelName1,'NavAdder');
				Module1 = Class<Actor>( DynamicLoadObject(LevelName1, class'Class',True) );
				if ( Module1 == None )
				{
					log ("Old common modules unavailable...",'NavAdder');
				}
				else
				{
					log ("Found an old tweaking module, attempt loading... "$Module1,'NavAdder');
					Spawn(Module1);
					if ( Module1 != None )
					{
						if ( Level.NetMode == NM_ListenServer || Level.NetMode == NM_DedicatedServer )
							AddToPackageMap( string(Module1.Outer.Name) );
						log ("Old Module has been started...",'NavAdder');
					}
					else
					{
						log("BORK - UNABLE To Load old Module !",'NavAdder');
					}
				}
Last edited by sektor2111 on Sun Feb 28, 2021 7:46 am, edited 1 time in total.

Eternity
Average
Posts: 48
Joined: Sat Nov 30, 2019 10:56 pm

Re: How to get class reference from name variable

Post by Eternity » Sun Feb 28, 2021 7:43 am

1337GameDev wrote:
Sun Feb 28, 2021 6:34 am
Isn't this used to instantiate an instance of a given object, by name, not get a class<Class1> style reference?
Hmm, does "DynamicLoadObject" create an instance? It seems not...
It returns a class reference from the class name given as a String input parameter (and prior to this it loads corresponding object from the package).
But it is not necessary to use the class references in order to perform inventory checks. There are else ways to do this...
Some examples here, with different options to consider for performing the checks in an appropriate way: pastebin.com/UMkGM6q1 (using "Instance.IsA()", or "Instance.Class.Name", and keeping in mind the logic of these comparison ways in particular cases).

User avatar
Feralidragon
Godlike
Posts: 5300
Joined: Wed Feb 27, 2008 6:24 pm
Personal rank: Work In Progress
Location: Liandri

Re: How to get class reference from name variable

Post by Feralidragon » Sun Feb 28, 2021 4:03 pm

In that case, is exactly like I said in my post, just use IsA:
https://www.madrixis.de/undox/Source_co ... t.html#283

DynamicLoadObject is only meant to load objects dynamically from a string (not a name), with this string being the full reference to that object (package + name).
Class is also itself an Object (just like in Java, everything is an object extending from Object, whether this is exposed by UScript or not [native]), meaning that one of the things it can load is indeed a class reference, but it can load pretty much any kind of object.

I have seen mentioned that dynamic loading objects is slow... well, it is, but that's why you structure your code to load them only once.
I use dynamic loading in NW3 to load all the assets I need to load (to keep these assets configurable by the end-user), but this happens only once at the start.
You don't need to dynamically load anything more than once.

However, either way, for your specific use case this dynamic loading won't get you anywhere close to what you want, because you don't want to load classes, you just want to know if an actor class "is a" subclass or the class itself specified through its name, and for that you use IsA, and you don't need anything else, it's that simple.

1337GameDev
Experienced
Posts: 85
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: How to get class reference from name variable

Post by 1337GameDev » Tue Apr 13, 2021 5:18 am

Hmm, im looking at this issue again.

I'm currently wanting to provide a list of strings/names to Actor.ForEach to dynamically look at Actors of a specific class, around a particular actor.
eg:

Code: Select all

local string ClassNamesToLook[8];
local int i;
local float r;
local Actor a;

r = 30.0;

for(i=0; i<32; i++) {
    foreach VisibleCollidingActors(class 'ClassNamesToLook[i]', a, r, self.Location) {//obv I cannot access the class name like this
        //do something with the found actor here - based on class name
    }
}

Eternity
Average
Posts: 48
Joined: Sat Nov 30, 2019 10:56 pm

Re: How to get class reference from name variable

Post by Eternity » Tue Apr 13, 2021 6:27 am

It does not work this way...

Also, if there is an opportunity (depending on the particular task) - it is better to avoid the use of String type variables in this case, as it is the slowest option.
It is better to use either class<Actor> type variables (to store a full class references, e.g.: class'PackageName.ClassName'), or Name type variables (to store only short names, e.g.: 'ClassName').

And it is better to call "VisibleCollidingActors" only once and only for the class'Actor', and then within the "foreach" loop to perform the checks for the specific classes, because this function may work relatively slow in some cases (if Radius would be much higher than 30...).

Code: Select all

local class<Actor> ClassNamesToLook[8];
local int i;
local float r;
local Actor A;

r = 30.0;
foreach VisibleCollidingActors(class'Actor', A, r, self.Location)
{
	for (i=0; i<ArrayCount(ClassNamesToLook); i++)
		if (A.Class==ClassNamesToLook[i])
			Break;

	if (i==ArrayCount(ClassNamesToLook))
		Continue;

       	// do something with...
}

Code: Select all

local Name ClassNamesToLook[8];
local int i;
local float r;
local Actor A;

r = 30.0;
foreach VisibleCollidingActors(class'Actor', A, r, self.Location)
{
	for (i=0; i<ArrayCount(ClassNamesToLook); i++)
		if (A.Class.Name==ClassNamesToLook[i])
			Break;

	if (i==ArrayCount(ClassNamesToLook))
		Continue;

       	// do something with...
}
For the case if also need to check the parent classes, there are such functions as "ClassIsChildOf" (for the class references) and "IsA" (for the names)... So, then, it should be ClassIsChildOf(A.Class, ClassNamesToLook[i]) and A.IsA(ClassNamesToLook[i]) respectively, instead of A.Class==ClassNamesToLook[i] and A.Class.Name==ClassNamesToLook[i].

1337GameDev
Experienced
Posts: 85
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: How to get class reference from name variable

Post by 1337GameDev » Wed Apr 14, 2021 2:04 am

Makes sense. I wouldn't be calling the actor collision lookup much (it'd be on a timer, set at once every second or so). Can these class references be set pretty easily in the editor?