Synthorange Posted March 22, 2016 Share Posted March 22, 2016 Heya. So I recently made a few mods. http://steamcommunity.com/sharedfiles/filedetails/?id=648861674 and http://steamcommunity.com/sharedfiles/filedetails/?id=648913550 which give the skyranger different skins. The mod currently does this through brute force, I located all the cinematic and avenger map files that reference the skyranger and swapped it out with my own model that references my own textures. After that all I need to do is replace the materials for new looks. This is definitely not optimal. Having all the skins in one mod with a way to pick between them would be great instead of having one mod for each skin. So I'm aiming at a mod that has1. Has a bunch of material sets2. A simple two button addition to the UI on the squad equip screen to toggle between them.3. The bit that locates the skyranger actor and swaps its skin. So I started looking up stuff. While trying to find out how the skyranger is even referenced from the game, I found that a lot of the time it's a foreach AllActors (look for actor with tag.Cinematic_Dropship). Also the cinematic dropship is always SkeletalMeshActor_8 in all kismet maps. SkeletalMeshActor_0 for the avenger dropship. I've no idea if I only need to do it once per game, or each time a new scene is loaded. If the next bit I was working on actually worked I'd be able to tell. I'm not any sort of programmer, I'm an artist who's just soaked up this stuff. :v: Armed with a very basic knowledge I banged out some stuff: class UISL_AwesomeSkyranger extends UIScreenListener; // This event is triggered after a screen is initialized event OnInit(UIScreen Screen) { local Skyranger_Customizer Skycustom; `log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); `log(""); `log("LISTEN!"); `log(""); `log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); Skycustom.FindandSkinSkyranger(); } Which is supposed to call on: class Skyranger_Customizer extends Actor config(SOSkyranger); var MaterialInstanceConstant Mat_Hull; var MaterialInstanceConstant Mat_Glass; var MaterialInstanceConstant Mat_Interior; var MaterialInstanceConstant Mat_Engine; var MaterialInstanceConstant Mat_Landing; static function FindandSkinSkyranger() { local SkeletalMeshActor A; `log("FINDING SKYRANGER!"); foreach AllActors(class'SkeletalMeshActor', A) { if ( A.Tag == 'CinematicDropship' || A.Tag== 'AvengerSideView_Dropship') { `log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); `log(""); `log("SWAPPING SKYRANGER SKIN!"); `log(""); `log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); Unit.SkeletalMeshComponent.SetMaterial(1, Mat_Hull); Unit.SkeletalMeshComponent.SetMaterial(2, Mat_Glass); Unit.SkeletalMeshComponent.SetMaterial(3, Mat_Interior); Unit.SkeletalMeshComponent.SetMaterial(4, Mat_Engine); Unit.SkeletalMeshComponent.SetMaterial(5, Mat_Landing); } } } defaultproperties { ObjName="Skyranger Customizer" Mat_Hull = MaterialInstanceConstant'SO_Skyranger.Materials.Skyranger_Hull_MATINST' Mat_Glass = MaterialInstanceConstant'SO_Skyranger.Materials.Skyranger_Hull_MATINST' Mat_Interior = MaterialInstanceConstant'SO_Skyranger.Materials.Skyranger_Hull_MATINST' Mat_Engine = MaterialInstanceConstant'SO_Skyranger.Materials.Skyranger_Hull_MATINST' Mat_Landing = MaterialInstanceConstant'SO_Skyranger.Materials.Skyranger_Hull_MATINST' } Which falls down at the first point of trying to foreach inside a static, so I cant even get to the point of testing out material resetting. It can probably all go in the trash though haha. I dont get so many things about unrealscript! I'm guessing I'm going to read up more on templates? Link to comment Share on other sites More sharing options...
zingfharn Posted March 22, 2016 Share Posted March 22, 2016 (edited) Theres a couple of ways you could do this (and you only need to do it once per game). Set the various versions in an inifile, and just have the person swap fiddle in the ini file to choose which they'd like, but that's a littlle ghetto. If you don't want to do it that way, and I don't blame you if you don't, then here are some things that might help. First of all, you can probably add this into in the screenlistener at the bottom. It'll stop it being called on every screen, although it won't work if someone loads into tactical, I guess. But you could argue there's no paint in the field, or something ; ) defaultproperties { ScreenClass = UIAvengerHUD; } To get your function working, I had to do some digging, but I got there eventually (and I'm sure I'll need it at some point, so double-win) drop the "static" from here "static function FindandSkinSkyranger()" And in the screenlistener, do this local SkyTest Sky; Sky = Screen.Movie.Pres.Spawn(class'SkyTest'); Sky.FindandSkinSkyranger(); Except replace SkyTest with your classname. I'm pretty new so Unreal, so I'm not sure how it handles memory - you might need/want to destroy Sky afterwards (with something like Sky.Destroy()). you could also declare at the top of the screenlistener something like local SkeletalMeshActor AvengerRanger, CineRanger; And instead of one loop to find and set both, do something like function FindCineRanger() { foreach AllActors(class'SkeletalMeshActor', A) { if ( A.Tag == 'CinematicDropship') { return A; } } }Then in the screenlistener do CineRanger = Sky.FindCineRanger();and repeat for AvengerRanger, with a different tag match. Or, if you wanted to get really snazzy, you could do function FindARanger(name Ranger) { foreach AllActors(class'SkeletalMeshActor', A) { if ( A.Tag == Ranger) { return A; } } } and call it with CineRanger = Sky.FindARanger('CinematicDropshop'); AvengerRanger = Sky.FindARanger('Sideviewwhatever');Then! In the future, when people are fiddling with your button, you can just directly reference the CineRanger and AvengerRanger variables, without having to loop again.And you can just do (presumably), CineRanger.SkeletalMeshComponent.SetMaterial(1, Mat_Hull); and so on. You can also refactor that code to be a little neater by having a function which does something like function ReSkin(SkeletalMeshActor Sky) { Sky.SkeletalMeshComponent.SetMaterial(1, Mat_Hull); .. .. } and then just call ReSkin(CineRanger); ReSkin(AvengerRanger); Hopefully that makes sense. If not, please do feel free to ask anything that's unclear. Edited March 22, 2016 by zingfharn Link to comment Share on other sites More sharing options...
Synthorange Posted March 22, 2016 Author Share Posted March 22, 2016 (edited) Awesome thanks. Looks straightforward and I understood most of it. :v I'll have a crack at it again and see what I manage to break. Oh god I'm going to have to remember how to set up structs and how set up an array of them for the material sets arent I. Oh there's this I guess: local SkyTest Sky; Sky = Screen.Movie.Pres.Spawn(class'SkyTest');So it's not enough just to declare it a local Skycustomizer, I've no idea what anything on the second line meant except Sky and class Skytest. Edited March 22, 2016 by Synthorange Link to comment Share on other sites More sharing options...
zingfharn Posted March 22, 2016 Share Posted March 22, 2016 Heh. Structs are easy (I'd never really used them before) struct Cheese { var string strength; var string name; }; var array<Cheese> AllTheCheeses; If you want to load it from a config then it's var config array <Cheese> Cheeses; And in the ini file do this .Cheeses = ( name = "Cheddar", strength = "weak") .Cheeses = ( name = "Brie", strength = "unquestionable") etc etc. the "." at the start is vital, despite all the docs saying it's not. Link to comment Share on other sites More sharing options...
Synthorange Posted March 22, 2016 Author Share Posted March 22, 2016 Thanks. Also have no idea what this does: Sky = Screen.Movie.Pres.Spawn(class'SkyTest'); Well, I mean it assigns sky properly, but I thought the local skytest sky would have done this. Anyway I've gotten it all to the point where I'm in game and swapping out all the materials. Using logs and GetMaterial I KNOW that the materials have been updated. But it's still showing the old materials. What the hell?! Link to comment Share on other sites More sharing options...
zingfharn Posted March 22, 2016 Share Posted March 22, 2016 (edited) Well, Sky on it's own won't do anything (I know, I have no idea why) and you can't use new, because it's an actor, and screen listeners don't know about spawn, directly. So you're basically saying Sky is definitely, really a SkyObject, and because, Mr Screenlistener, you're utterly hopeless, this is where the spawn function lives (i did some searching and a lovely lady called Heather had blogged about it). Are you adding in the materials in the ini file? Do you need to do that? graphics is where I get a bit lost. Like, I had jacked in a new Tygan Pawn and had to put this in XComContent.ini +BodyPartTemplateConfig=(PartType="Pawn", TemplateName="XCOM_Tygan_Redux", ArchetypeName="Tygan_Head.ARC_Unit_HeadScientist", Gender=eGender_Male, bVeteran=false, ArmorTemplate="", CharacterTemplate="HeadScientist", SpecializedType=true) Then I could refer to XCOM_Tygan_Redux in the code. But maybe because you're calling it directly, it's different. I also found for tygan, I had to be crafty and bounce him in and out of the shadowchamber to force an update, because he didn't do it by default. Maybe try and hide and reshow the thing to force a visual update? Or does Actor have some kind of basic forceRefresh method you can call? You could, of course, remove the UIAvengerHuD thing from ScreenClass (set it to none) - that will make it trigger all the damn time, but it might help you track down the problem. Edited March 22, 2016 by zingfharn Link to comment Share on other sites More sharing options...
Synthorange Posted March 22, 2016 Author Share Posted March 22, 2016 Actually it works! It's the method I was using to locate the actors that was not reliable! It turns out the tags arent really a good way to locate them and searching by skeletalmesh.name is a way better idea because I know exactly what it is. http://i.imgur.com/F3b8Eqr.jpg One REALLY SERIOUS problem though. On returning to the avenger, it's stuck in a broken view of the ship! I assume the UIScreenListener on the UIAvengerHUD is messing with things somehow? Link to comment Share on other sites More sharing options...
zingfharn Posted March 22, 2016 Share Posted March 22, 2016 Stuck in a broken view of the ship how? The picture above doesn't tell me much. Unless you mean it just hovers above the avenger like that? My first guess might be animation sets? But that's my default answer for anything graphics. So it's probably not. But maybe. the screenlistener shouldn't mess with anything. It'll just fire every time you hit the avenger hud, so at worst, it's just changing the thing to the same thing every so often (check the log file to see when). Also, we can talk about removing the repeated calls later, when you're happy it works properly. That's good for optimising your mod. Anyway, can you describe the problem (or grab a vid, to see it in motion) and we can go from there (or someone else can pick it up). Link to comment Share on other sites More sharing options...
Synthorange Posted March 22, 2016 Author Share Posted March 22, 2016 http://i.imgur.com/2MMhOTp.jpg This is the view it's stuck on immediately after exiting the Skyranger sitting around postmission screen. Nothing else. Activity seems to be going on but I cant move out of it apart from pulling up the esc menu. Link to comment Share on other sites More sharing options...
Synthorange Posted March 22, 2016 Author Share Posted March 22, 2016 Current code: class UISL_AwesomeSkyranger extends UIScreenListener; var Skyranger_Customizer Sky; var SkeletalMeshActor SkyAv, SkyCin; // This event is triggered after a screen is initialized event OnInit(UIScreen Screen) { Sky = Screen.Movie.Pres.Spawn(class'Skyranger_Customizer'); SkyCin=Sky.FindSkyrangerCinematic(); SkyAv=Sky.FindSkyrangerAvenger(SkyCin); Sky.Reskin(SkyCin); Sky.Reskin(SkyAv); } defaultproperties { ScreenClass = UIAvengerHUD; } class Skyranger_Customizer extends Actor config(SOSkyranger); var MaterialInstanceConstant Mat_Hull; var MaterialInstanceConstant Mat_Glass; var MaterialInstanceConstant Mat_Interior; var MaterialInstanceConstant Mat_Engine; var MaterialInstanceConstant Mat_Landing; function ReSkin(SkeletalMeshActor Sky) { Sky.SkeletalMeshComponent.SetMaterial(0, Mat_Hull); Sky.SkeletalMeshComponent.SetMaterial(1, Mat_Hull); Sky.SkeletalMeshComponent.SetMaterial(2, Mat_Glass); Sky.SkeletalMeshComponent.SetMaterial(3, Mat_Interior); Sky.SkeletalMeshComponent.SetMaterial(4, Mat_Engine); Sky.SkeletalMeshComponent.SetMaterial(5, Mat_Landing); } function SkeletalMeshActor FindSkyrangerCinematic() { local SkeletalMeshActor Ranger,A; foreach AllActors(class'SkeletalMeshActor', A) { if (A.SkeletalMeshComponent.SkeletalMesh.name== 'SM_Skyranger') { Ranger = A; return Ranger; } } } function SkeletalMeshActor FindSkyrangerAvenger(SkeletalMeshActor SkyCin) { local SkeletalMeshActor Ranger,A; foreach AllActors(class'SkeletalMeshActor', A) { if ( A.SkeletalMeshComponent.SkeletalMesh.name== 'SM_Skyranger' && A.Tag!='CinematicDropship') { Ranger = A; return Ranger; } } } defaultproperties { Mat_Hull = MaterialInstanceConstant'Skyranger.Materials.Skyranger_Glass_MATINST' Mat_Glass = MaterialInstanceConstant'Skyranger.Materials.Skyranger_Glass_MATINST' Mat_Interior = MaterialInstanceConstant'Skyranger.Materials.Skyranger_Glass_MATINST' Mat_Engine = MaterialInstanceConstant'Skyranger.Materials.Skyranger_Glass_MATINST' Mat_Landing = MaterialInstanceConstant'Skyranger.Materials.Skyranger_Glass_MATINST' } Link to comment Share on other sites More sharing options...
Recommended Posts