Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Rocko Bonaparte
Mar 12, 2002

Every day is Friday!

emanresu tnuocca posted:

blueprint communication
I may have looked at the online stuff on that one. This is what I looked at tonight:
https://docs.unrealengine.com/5.3/en-US/how-to-use-blueprint-communications-in-unreal-engine/

I could not get the interface to plumb as a variable, but I could do it for an actor. The example in that document is using actors. When I exposed an a public variable for an actor, I did get a field where I could click on an eyedropper and select the notification cube that implements the desired interface as the target actor.

The only problem is that nothing seems to be crossing over. In my event graph, I have "Target as Actor" originating the variable into the blueprint. I then cast it to IBooleanToggle. I have it printing a note if the cast failed. I don't see that note. I do see the note that it succeeded. I tried to make this the target for my On Boolean Activated interface function, but it inserted an object conversion in between. I have a print on the receiving blueprint for the call and it never prints.

I could be doing dumb stuff in the blueprint. I have the interface function toggled by my collision logic but the target is coming in from those casts being initiative from BeginPlay. I did chain a print statement after the call and that print does happen. I just wonder if something in all the casting is going to hell so the interface function just gets ignored.

Regarding an event: That's going back in a circle, so I have to just laugh at my life there. But if I following that web page correctly, that event would be like some global message. I couldn't tell how I could have two independent floor plates firing their own events with their own stuff listening to them. I just didn't see anywhere in the example where it was doing some kind of specific binding like that. It was working with the character in the examples and just grab the character like that was the only thing that could ever matter. After seeing multiple examples of events being only like that, I've had to assume they're too global for what I want. I'd have to use them to fire some identifier and everything listening to it would have to determine if its for them. I wouldn't want to do that. I also just hope I'm wrong about it.

Anyways, it's late and I can't wring it any further so I'll look for local samples tomorrow. Then I can poke something with a stick that appears to work and try to come to terms with how.

Adbot
ADBOT LOVES YOU

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
What's wrong with having a "red keycard grabbed" event that unlocks the red doors, and a separate "blue keycard grabbed" event that unlocks the blue teleporter? Are you sure you're not just overcomplicating things for no reason?

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I am going for something like StarTropics with tiles triggering all kinds of stuff all over the place.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
Are you concerned about having one event dispatcher per interaction being too many?

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
My preoccupation with them is not being able to disambiguate them, and then having to rely on some hard-coded ID, which I have a habit of screwing up compared to literally assigning the two object/actor/entities to each other. Before I continue, I may not be understanding the limitations in the first place so this could just be garbage. The floor tile is something I intend to repeat more than twice (maybe upwards of 50 times in a level?), but twice is more than I see from even dispatcher examples. Let's say I have something like:

1. Floor tile A1 controlling stone sliding door B1.
2. Floor tile A2 controlling stone sliding door B2.

If the event is global, I now need to add some kind of ID to the event so when doors B1 and B2 see it, they need to tell if its for them. I'd have to use some kind of ID, and that's a kind of indirection I'm more prone to screw up than, say, literally grabbing door B1 from the editor and dropping it on floor tile A1's switch toggle field. Maybe I can do that drag-and-drop and extract some ID. I dunno, but that seems to be an extra level of work because I was assuming I could somehow make this assignment in Unreal.

Alternately, I'm somehow creating a dispatcher for each tile. At this point, I am editing the component script for the floor tile as a commodity, so I don't know how I'd create a unique one for each copy I make. Would I have to go into each blueprint for each clone and edit the dispatcher/listener?

Like, how does anything interact with any specific thing in this engine? It absolutely cannot be this difficult and I don't know something.

Edit: At any rate, I was going to go digging around examples later tonight and see if I can find the blueprint interaction stuff. Then I can just meddle with something that starts out as working and I can see what breaks or goes strange.

Rocko Bonaparte fucked around with this message at 15:06 on Oct 5, 2023

jizzy sillage
Aug 13, 2006

You can just do the Unity thing of "create reference, assign reference in inspector, now two objects are linked in this level".

Raenir Salazar
Nov 5, 2010

College Slice
Wish I could help more as Unreal is my tool for my day job but I at my work basically 99% of the time only work in C++.

ZombieApostate
Mar 13, 2011
Sorry, I didn't read your post.

I'm too busy replying to what I wish you said

:allears:
Quick and dirty example. 2 Actors, Door and Trigger. Add a Trigger variable to Door of the TriggerBP type and make it instance editable. Set the trigger on each instance in the map editor and done. Unless I forgot to mention something.

Door (note the bit circled in red, it won't show up when editing the instance in the map editor if it's not instance editable):


Trigger:


https://i.imgur.com/yzTR72l.mp4
edit: imgur butchered it, but the bit where I click on a dropdown is changing the trigger on the green door to the orange trigger so they both pop off orange.


edit2: gently caress it have another screenshot :argh: The bit circled in red is where you set the trigger.

In theory you can just click the '+' next to On Trigger Activated/Deactivated on the bottom right under Events on the Door and use what it puts in the graph instead of manually binding them in BeginPlay like I did, but it was bugging out unless I restarted the editor after I changed the trigger on the instance every time. Gonna have to report that, I think.

edit3: apparently compiling the blueprint after changing the trigger in the map editor also fixes it, so you don't have to restart the editor at least.

You could also do this with an Interface and skip the event stuff, as was mentioned.

ZombieApostate fucked around with this message at 20:19 on Oct 5, 2023

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I'll go back and try a dispatcher tomorrow, but I got the interface to work tonight. This despite getting my Covid booster and turning into a pumpkin. I had downloaded and run the stock blueprint communications project and reviewed how it was doing interfaces.

When they connected the actor to the interface call, it just worked. When I originally did it, it wouldn't cast. Then I did it again and it just kind of worked. I didn't have to convert it to my interface type, get an object reference, and pass that to the interface call. I just worked correctly when I went back to it.

On the listening side, my first attempt was actually attempting to call the interface function. My second attempt was... actually, I think it was still trying to call the function but by using the interface type. Finally, I found the event implementation for the interface (what in flying gently caress?!) and that properly invoked my listener code.

So, I guess I'm implementing my interface by using events. Like, on one hand it makes sense, but when I was getting some advice in the middle to not use events, but use interfaces instead (some Discord help here; nobody take that personally), that was really confusing.

Chillmatic
Jul 25, 2003

always seeking to survive and flourish
That’s a weird quirk of interfaces, yeah. When you create a BPI function that doesn’t return anything, it will implement as an event.

If you create a BPI function that does return something, it will implement as a function (because events can’t return data).

TooMuchAbstraction
Oct 14, 2012

I spent four years making
Waves of Steel
Hell yes I'm going to turn my avatar into an ad for it.
Fun Shoe
For your "it didn't work the first time, but did work the second", perhaps the first time you didn't compile before trying to hook things up? Blueprints require you to compile regularly before bits you've written really become "known" to the editor.

Chillmatic
Jul 25, 2003

always seeking to survive and flourish
I'm still confused about the whole 'setting the interface as a variable' thing, which is something I've never done or even heard of in several years of using UE professionally.


BPIs are very simple to use. You create the BPI, then inside it you add the function you want, then implement both the interface and the function you made on the actor you want to do stuff with or to, then you call that function from another class which will make all this to happen (like a trigger volume or whatever).

You don't need to (and should not) cast anything. All you need is a generic reference to the class that has the interface and functionality on it (or one of its parents) and call the BPI function from that reference.

The nice (and for beginners, kind of confusing) thing is that if you call a BPI function on a class that doesn't implement it, it will fail silently, which is almost always what you want. If necessary you can check for failure by using the node "Does implement interface" before actually making the BPI call, so you know for certain why something isn't working.

If all this is still confusing let me know and I'd be happy to take some screenshots or a quick video or something. BPIs are awesome, straightforward, and how I handle the vast, vast majority of class communication (outside of one-to-many situations where event dispatchers work best).

Chillmatic fucked around with this message at 16:01 on Oct 6, 2023

Chillmatic
Jul 25, 2003

always seeking to survive and flourish

ZombieApostate posted:

Quick and dirty example. 2 Actors, Door and Trigger. Add a Trigger variable to Door of the TriggerBP type and make it instance editable. Set the trigger on each instance in the map editor and done. Unless I forgot to mention something.

Door (note the bit circled in red, it won't show up when editing the instance in the map editor if it's not instance editable):


Trigger:


https://i.imgur.com/yzTR72l.mp4
edit: imgur butchered it, but the bit where I click on a dropdown is changing the trigger on the green door to the orange trigger so they both pop off orange.


edit2: gently caress it have another screenshot :argh: The bit circled in red is where you set the trigger.

In theory you can just click the '+' next to On Trigger Activated/Deactivated on the bottom right under Events on the Door and use what it puts in the graph instead of manually binding them in BeginPlay like I did, but it was bugging out unless I restarted the editor after I changed the trigger on the instance every time. Gonna have to report that, I think.

edit3: apparently compiling the blueprint after changing the trigger in the map editor also fixes it, so you don't have to restart the editor at least.

You could also do this with an Interface and skip the event stuff, as was mentioned.



Oof, yeah this is...not the way to do this sort of thing. Not trying to be a jerk but event-driven logic is massively overcomplicated for a simple one-to-one communication use case. You also want to avoid hardcoding references in the instance like that (your red circle), because now all other logic depends on that assumption and changing things becomes a pain in the rear end.

Chillmatic fucked around with this message at 15:58 on Oct 6, 2023

emanresu tnuocca
Sep 2, 2011

by Athanatos
Yeah I was also a little confused about that, here's a very simple way to get a trigger you can point to a specific door, no instances no nothing, I'm not using the 'Begin Overlap Event' cause I created a blank project and don't have a character, so.

Only two actors, "BP_Trigger" this guy receives the "Debug Key Tab" (gotta make sure it has 'consume input' enabled) and has a reference to a door, as you can see this is just a variable of type "BP_Door" that is instance exposed.



Now BP_door just has an event called "Open This Door Right Here" that is a simple timeline that adds some offset to make it work.



Now in the level I drag in the trigger actor and two BP_Door actors



Notice the 'default' DOOR: NONE thingie in the details view, as long as this is not assigned to anything pressing tab will just throw an error 'attempted to access none', but once I select one of my instances I can press tab and this will make my 'door' fly up for five seconds.





And yeah, if you need generic buttons that trigger some generic interaction that may apply to a variety of actors, that's where you'd potentially want to use an interface, unless all of your interactables have a shared parent that exposes the interaction event/function in some other way, but really if you just want buttons that open doors that's all there is to it. The level designer is still able to assign doors to buttons without touching the blueprints, and all that.



Apologies if I was missing some other finer point in the discussion and this is not helpful.

Chillmatic
Jul 25, 2003

always seeking to survive and flourish
You're hardcoding not only a specific instance of a specific door, but also assuming the class "BP_Door" will always be the class that this is called on.

This can be done with just a few nodes, no assumptions, and no instance-specific references or hardcoding of any kind.

I think I'll make a short video showing optimal class-to-class communication because the solutions I often see employed are either over-engineered or else brittle and full of assumptions.

Chillmatic fucked around with this message at 18:27 on Oct 6, 2023

emanresu tnuocca
Sep 2, 2011

by Athanatos
an instance editable variable is not hard coded.

Chillmatic
Jul 25, 2003

always seeking to survive and flourish

emanresu tnuocca posted:

an instance editable variable is not hard coded.

Instance references must be set by hand, can't be changed at runtime, and have to be specifically set with something or else return null errors. We may have different definitions of what hardcoded means, but this is what I'm referring to. Sometimes you can't avoid it, but I'm not seeing where it makes sense to do this for simple class communications.

emanresu tnuocca
Sep 2, 2011

by Athanatos
if you have a simple game that has various buttons/triggers that open specific instances of doors in your level this is just about the easiest way to link the right buttons to the right doors on the level of the level editor, which may be desired. And of course it can be changed during run time, if I need my trigger to point to different doors all it would take is setting the 'door' variable to a different instance of BP_Door, or anything that inherits from BP_door.

the use case wherein you have a certain trigger in the level that opens a specific door really seems to me to be just about the exact case where you'd definitely expect to set the reference by hand. This really has nothing to do with how complicated the communications are, but rather with the use case of... specific trigger that opens a specific door. I mean. I don't know, really, given the original question I was trying not to complicate things, unreal has many ways to achieve things.

Chillmatic
Jul 25, 2003

always seeking to survive and flourish

emanresu tnuocca posted:

if you have a simple game that has various buttons/triggers that open specific instances of doors in your level this is just about the easiest way to link the right buttons to the right doors on the level of the level editor, which may be desired.

You're right about that. I think I'm just used to approaching more systemically because bigger games essentially force you to, and working with a large team you have to design things so stuff doesn't break when someone changes something.

blastron
Dec 11, 2007

Don't doodle on it!


I’m not following this thread super closely, but has anyone suggested event dispatchers and delegates? These are the easiest way to have level-specific logic where an object X triggers events that in turn trigger behaviors on objects Y and Z. The ideal implementation of these is to have a single actor in your level that exposes a bunch of delegates that listener objects can bind to, then a bunch of functions that call those delegates, which triggering objects will themselves call. This gives you a centralized object that you can pretty easily expand by both adding new delegate to handle new events and also adding new callers/subscribers to hook new things into the system.

ZombieApostate
Mar 13, 2011
Sorry, I didn't read your post.

I'm too busy replying to what I wish you said

:allears:

Chillmatic posted:

Oof, yeah this is...not the way to do this sort of thing. Not trying to be a jerk but event-driven logic is massively overcomplicated for a simple one-to-one communication use case. You also want to avoid hardcoding references in the instance like that (your red circle), because now all other logic depends on that assumption and changing things becomes a pain in the rear end.

I did say you could skip the event stuff and use an interface instead :v:

Basically I just wanted to show that what Rocko was trying to do originally does actually work, and maybe with a simple example they could see what pitfall they fell into. And kind of sloppily demonstrate that you could then easily have one trigger activate multiple other things.

Chillmatic posted:

You're hardcoding not only a specific instance of a specific door, but also assuming the class "BP_Door" will always be the class that this is called on.

This can be done with just a few nodes, no assumptions, and no instance-specific references or hardcoding of any kind.

I think I'll make a short video showing optimal class-to-class communication because the solutions I often see employed are either over-engineered or else brittle and full of assumptions.

I would actually like to see this, it's always nice to see how other people do things.

Chillmatic
Jul 25, 2003

always seeking to survive and flourish
There are definitely multiple ways to do things in gamedev, and I know better than to make it sound like there's a One True Way. Sorry about that, y'all.

I think I get frustrated because when I was starting out the tutorial scene was full of terrible practices and people just 'doing stuff' without explaining why, and for all I know it's still like that. Most likely I'd have benefited from more structured advice like "use X when you're trying to do Y", which is really what I've been doing sub-optimally, here.

ZombieApostate posted:

I would actually like to see this, it's always nice to see how other people do things.

Sure, I can put something together. Does anyone else have questions or want to see something covering how to communicate between classes, when to use what, etc?

jizzy sillage
Aug 13, 2006

Just make videos about everything and don't stop, then post them here for us :)

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!
I think an important thing to do a lot of the time is think through what abstraction *makes sense*.

Like if you're just going to be manually crafting levels with custom interactions between things then all blueprints whatever mechanism they use makes sense.

But if you're envisioning something more like a puzzle sandbox thing where you might want a level editor, then it maybe makes more sense to have some kind of common "trigger" action abstraction, which has an object that fires it (the button, but maybe also levers or pressure plates or tripwires or heat sensors or whatever) and an object or objects that receive said trigger action (a door, a spike, a gun).

Also relevant to consider, is it a trigger or is an on/off state or can you configure the same abstraction to work different ways (like a button can be "the door is open while you hold it" or it can be "the door state toggles when you press it"; different intentions that can both be represented by a button and a door.)

And then you might also consider, do you want the connection to be visible in the world? Because if you do maybe you don't even need to connect button and door together programmatically, you might have buttons perform a trigger/toggle action on any wires that are touching its contact points, and wires transmit signal to whatever object is at the other end.

Chillmatic
Jul 25, 2003

always seeking to survive and flourish
Of course, but it's also important to cover fundamentals before delving into things you may or may not end up needing. That's why I've been talking about setting up even simple systems as generically as possible, so they're easier to grow and modify smoothly alongside the game you're working on.

When I was starting out, few things frustrated me as much as having to revisit some old system I'd made previously, and hitting a brick wall full of assumptions and magic numbers and hard casts and giant methods/classes and pretty much all the big bad smells.

more falafel please
Feb 26, 2005

forums poster

Chillmatic posted:

Of course, but it's also important to cover fundamentals before delving into things you may or may not end up needing. That's why I've been talking about setting up even simple systems as generically as possible, so they're easier to grow and modify smoothly alongside the game you're working on.

When I was starting out, few things frustrated me as much as having to revisit some old system I'd made previously, and hitting a brick wall full of assumptions and magic numbers and hard casts and giant methods/classes and pretty much all the big bad smells.

Yeah, but just as annoying is the super overengineered perfectly abstract system where you have to hook up 17 objects, write some custom callbacks (but first define a new CallbackReceiver subclass), add two new global enums, and adjust the behavior of a state machine just to call a function. That stuff also gets super difficult to debug, profile, reason about, change, or document too.

I tend to prefer code/systems that are easier to delete if what I need it to do changes way too much from the original requirements/design.

blastron
Dec 11, 2007

Don't doodle on it!


It's also important to consider what level of effort a given thing may actually merit. If the primary purpose of this is to get something off the ground real quick in order to support more interesting gameplay, then just do the hackiest thing that lets you do that, with the understanding that at some point soon you'll probably need to tear it up and build something more comprehensive.

Chillmatic
Jul 25, 2003

always seeking to survive and flourish

more falafel please posted:

Yeah, but just as annoying is the super overengineered perfectly abstract system where you have to hook up 17 objects, write some custom callbacks (but first define a new CallbackReceiver subclass), add two new global enums, and adjust the behavior of a state machine just to call a function.

who on earth suggested doing something like this?

blastron posted:

just do the hackiest thing that lets you do that, with the understanding that at some point soon you'll probably need to tear it up and build something more comprehensive.

I don’t disagree at all with getting gameplay in, first, and then optimizing/patterning shortly after.

Chillmatic fucked around with this message at 04:07 on Oct 7, 2023

TooMuchAbstraction
Oct 14, 2012

I spent four years making
Waves of Steel
Hell yes I'm going to turn my avatar into an ad for it.
Fun Shoe
One of the things I really liked about Unity was coroutines. Being able to have multiple different execution contexts, each with their own closures for tracking state, was really handy for doing things like "first I want to play this animation, then I want to update this game state, then I want to play this other animation, then..." kinds of scenarios. Way less complicated than extracting all of their state to class member fields and putting a state machine in that runs in the Update function.

I'm not on Unity any more, I'm learning Unreal. So I found the UE5Coro library, and tried to use it. Unfortunately, it likes to crash the editor if I exit PIE while there's still active coroutines. The underlying cause appears to be that the coroutines are still active, despite the game being over and a bunch of important state having been cleaned up. This leads to access violations. For example, in this Rider screenshot, you can see that the i and j variables are clearly inaccurate; the Grid UObject is also null:



UE5Coro has functions for forcing coroutines to run in the main game thread, and for detecting coroutine cancellation, neither of which fix the issue. I suspect that when you exit PIE, a lot of stuff is just being forcibly cleaned up/unloaded, and it’s on me to recognize and respond to that (hence why I’m not bugging the UE5Coro dev for help).

I tried overriding the AActor::EndPlay function, so that my own code could recognize that the game is ending, and exit coroutines early; that doesn’t help either.

What’s the correct way to recognize and respond to PIE ending? Actually, a more appropriate question is probably: what’s the correct way to recognize and respond to an object being unloaded? After all, I might well get similar crashes when loading a new level. I only have one level at the moment, so it hasn’t come up.

Thanks for any advice or suggestions!

For the sake of completeness, here’s the function that’s most consistently crashing for me. It’s detecting matches in a match-3 game, clearing matched tokens, waiting for new tokens to fall in, and then scanning again, recursively.

code:
UE5Coro::TCoroutine<> AGameBoard::InnerScanForMatches(FIntVector2 gravity, UMatchSequence* sequence) {
	co_await UE5Coro::Async::MoveToGameThread();
	bool topLevel = sequence == nullptr;
	if (topLevel) {
		sequence = NewObject<UMatchSequence>();
	}
	FSimultaneousMatchSet curSet;
	FTokenMatch curMatch;
	auto matchDirections = TArray<FIntVector2>{{0, 1}, {1, 0}};
	for (int i = inactiveBorderSize; i < UTokenGrid::BOARD_SIZE - inactiveBorderSize; ++i) {
		for (int j = inactiveBorderSize; j < UTokenGrid::BOARD_SIZE - inactiveBorderSize; ++j) {
			for (auto dir : matchDirections) {
				auto startToken = Grid->GetToken(i, j);
				curMatch.tokens.Add(startToken);
				curMatch.type = startToken->Type;
				// Scan in dir until we encounter edge-of-grid or a different token.
				for (int k = 1; k < UTokenGrid::BOARD_SIZE - inactiveBorderSize; ++k) {
					auto p = FIntVector2(i, j) + dir * k;
					if (p.X >= UTokenGrid::BOARD_SIZE || p.Y >= UTokenGrid::BOARD_SIZE ||
						Grid->GetToken(p.X, p.Y)->Type != startToken->Type) {
						// Ran out of match to make.
						break;
					}
					// Valid token to add to match.
					curMatch.tokens.Add(Grid->GetToken(p.X, p.Y));
				}
				if (curMatch.tokens.Num() < 3) {
					// Not a valid match.
					curMatch.tokens.Empty();
					continue;
				}
				// Successful match!
				curSet.matches.Add(curMatch);
				curMatch = FTokenMatch();
			}
		}
	}

	// Done scanning for immediate matches.
	if (curSet.matches.Num() == 0) {
		co_return;
	}

	sequence->AddSet(curSet);
	UE_LOGFMT(LogTemp, Log, "Made {0} matches", curSet.matches.Num());
	co_await ClearMatches(gravity, curSet);
	if (mustCancelCoroutines || UE5Coro::IsCurrentCoroutineCanceled()) co_return;
	co_await InnerScanForMatches(gravity, sequence);

	if (topLevel && sequence->matches.Num() > 0) {
		// TODO flow popped token particles to the player, then cast spells
	}
}

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I'm trying to ingest some of this now. I haven't tried the event dispatcher method yet although I plan to. I see some similarities between interfaces and custom events. It looks like with the interface, I have to define that and the actor has to implement it. The way those calls are implemented are essentially events. If I instead provide a specific class type, I would call custom events in the target class as if they were functions in the triggering blueprint. Is this about right so far?

I was about to do a separate proof-of-concept with custom events and no interface but I think I just got the gist there.

Chillmatic
Jul 25, 2003

always seeking to survive and flourish
Class-specific custom events are not related to what you're trying to do, and event dispatchers are a a very different concept, but godspeed.

blastron
Dec 11, 2007

Don't doodle on it!


TooMuchAbstraction posted:

One of the things I really liked about Unity was coroutines. Being able to have multiple different execution contexts, each with their own closures for tracking state, was really handy for doing things like "first I want to play this animation, then I want to update this game state, then I want to play this other animation, then..." kinds of scenarios. Way less complicated than extracting all of their state to class member fields and putting a state machine in that runs in the Update function.

I'm not on Unity any more, I'm learning Unreal. So I found the UE5Coro library, and tried to use it. Unfortunately, it likes to crash the editor if I exit PIE while there's still active coroutines. The underlying cause appears to be that the coroutines are still active, despite the game being over and a bunch of important state having been cleaned up. This leads to access violations. For example, in this Rider screenshot, you can see that the i and j variables are clearly inaccurate; the Grid UObject is also null:



UE5Coro has functions for forcing coroutines to run in the main game thread, and for detecting coroutine cancellation, neither of which fix the issue. I suspect that when you exit PIE, a lot of stuff is just being forcibly cleaned up/unloaded, and it’s on me to recognize and respond to that (hence why I’m not bugging the UE5Coro dev for help).

I tried overriding the AActor::EndPlay function, so that my own code could recognize that the game is ending, and exit coroutines early; that doesn’t help either.

What’s the correct way to recognize and respond to PIE ending? Actually, a more appropriate question is probably: what’s the correct way to recognize and respond to an object being unloaded? After all, I might well get similar crashes when loading a new level. I only have one level at the moment, so it hasn’t come up.

Thanks for any advice or suggestions!

For the sake of completeness, here’s the function that’s most consistently crashing for me. It’s detecting matches in a match-3 game, clearing matched tokens, waiting for new tokens to fall in, and then scanning again, recursively.

I don’t know anything about this coroutines library, but when I’ve previously had to do asynchronous programming using UE’s futures or tasks, I used the garbage collector and/or smart pointers to determine if something had been cleaned up on another thread. If you’ve got a UObject you’re working with, I would recommend storing its pointer in a TWeakObjectPtr and calling IsValid() on that. This will (I think) catch all cases where EndPlay has been called and game objects have been either deleted or are pending destruction.

e: If these coroutines are being contained within a UObject and you’re capturing a pointer to this, there is a convenience function that checks to see if a given raw pointer is valid and points to a live UObject: IsValid(const UObject*).

blastron fucked around with this message at 19:13 on Oct 7, 2023

emanresu tnuocca
Sep 2, 2011

by Athanatos

Rocko Bonaparte posted:

I'm trying to ingest some of this now. I haven't tried the event dispatcher method yet although I plan to. I see some similarities between interfaces and custom events. It looks like with the interface, I have to define that and the actor has to implement it. The way those calls are implemented are essentially events. If I instead provide a specific class type, I would call custom events in the target class as if they were functions in the triggering blueprint. Is this about right so far?

I was about to do a separate proof-of-concept with custom events and no interface but I think I just got the gist there.

You're on the right path, if it works it works.

When it comes to these sort of discussions you'll often find people who's contribution to the discussion generally seems to be "oh, you guys are not good enough programmers, I could do this better, with half the effort" and then kinda leave it at that and you kind of have to filter it out, especially when it eventually usually boils down to nitpicks about terminology.

The example I provided with "BP Door" shows the use of a custom event I created in the "BP Door" class, which as you noticed would have looked pretty similar to if we chose to implement an interface in BP_Door, at the end of the day the class will still expose an event/function which you can invoke.

Dispatchers are not 'completly unrelated' and in fact you're already using a dispatcher, the 'OnActorBeginOverlap' thingie that is provided by default in the 'box collision' component is an event dispatcher, like, that's what it is, that's why you can bind to it, when you implement this event you're binding a custom event to the event dispatcher.

You're on the right track. I'd also recommend reading up a bit about composition vs. inheritance.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!

emanresu tnuocca posted:

You're on the right track. I'd also recommend reading up a bit about composition vs. inheritance.

I know plenty well about that particular topic unless you're talking about in a very specific Blueprint context. To be honest, I would be programming in C++ right now, but it was repeated over and over again online to give Blueprint a chance first. So I'm trying to do a lot of stuff that I had done plenty well enough in Unity with Blueprint instead.

The main problem here is having to learn a proprietary node-based programming language with a lot of slight variations in names that cause things to bind incorrectly. It could be worse in that it could be a proprietary version of C++ that is oh-just-so-slightly-different.

At this point, I've made a second implementation using custom events bound to a specific object instance. I intend to use an interface for what I actually want, but because of all the different approaches getting bandied around (here and elsewhere), I figured I should try them. They all probably have their own purpose and I might as well get used to them. Next is a third implementation using an event dispatcher.

Something I've learned over and over about my own particular shortcomings with this style of programming is I keep doing something on either end of the communication (from output to input) where I don't match the names or convention correctly so they don't actually plumb. I just did it with the custom event version here too; I ended up somehow calling the wrong thing so nothing was transmitted. I want to hope there's some way I can trace and debug the incorrect function call going into oblivion because that would be very useful. It would also be a very simple thing to check in a regular programming language.

TooMuchAbstraction
Oct 14, 2012

I spent four years making
Waves of Steel
Hell yes I'm going to turn my avatar into an ad for it.
Fun Shoe
I'm trying to set up the most basic input on my Unreal project, but I'm just getting nothing. Here's the steps I've done:

1. Created an Input Mapping Context containing a single Input Action:


2. Set up my player controller to set up the Input Mapping Context in BeginPlay:


3. Started play and run showdebug enhancedinput in the console:


However, no matter what I do, Enhanced Input doesn't change its display (i.e. it never says Left Mouse Button - (true) -), and my event listener never fires.

To be clear, the context is getting properly set up (and e.g. the "PC add mapping" message is printed). It sees that the IA_Select action should fire when LMB is pressed. It just doesn't ever actually fire when LMB is pressed. Or if I bind it to spacebar and mash that key, still nothing.

TooMuchAbstraction fucked around with this message at 01:47 on Oct 9, 2023

emanresu tnuocca
Sep 2, 2011

by Athanatos
is the 'consume input' option enabled for this actor?

TooMuchAbstraction
Oct 14, 2012

I spent four years making
Waves of Steel
Hell yes I'm going to turn my avatar into an ad for it.
Fun Shoe
Turns out it was something to do with my custom player controller class. If I use the default player controller, then it works. :shrug: Sure would be nice if I could inspect the default player controller and see how it differs from the custom one I set up!

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I also plumbed the event dispatcher today. So if I'm trying to link up custom events, do I have to just make sure I type their names in correctly? I mean that I see where I create a custom event, and then it looks like I just gotta make sure I get the name right or nothing's going to happen. Is that how custom events are hooked up?

xzzy
Mar 5, 2009

lol, guess that's one way to address the unity pricing debacle:

https://www.businesswire.com/news/home/20231009494331/en/Unity-Announces-Leadership-Transition

Adbot
ADBOT LOVES YOU

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
Thankfully, they got a new guy with a lot of good experience with subscription pricing!


quote:

James M. Whitehurst previously served as Senior Advisor at IBM from July 2021 to May 2022 and President of IBM from April 2020 until July 2021. He joined IBM through the acquisition of Red Hat, a leading provider of open source enterprise IT products and services, where he served as Chief Executive Officer from January 2008 until April 2020. Prior to Red Hat, Mr. Whitehurst served in several leadership roles at Delta Air Lines, Inc. including as Chief Operating Officer from 2005 to 2007, Chief Network and Planning Officer from 2004 to 2005 and Senior Vice President, Finance Treasury and Business Development from 2002 to 2004.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply