First of all, Brushbuilder is a class under Object, so open UED and uncheck "Actor classes only."
Ah ha! Now you should see Object >> Brushbuilder! Beneath this class, all your hopes and dreams are just waiting to come true.
Brushbuilders are basically your means of executing UnrealScript inside the editor. This means that if you know some UScript, you can write utility functions in a couple of seconds that can LITERALLY SAVE YOU HOURS IN THE EDITOR. This is especially useful if you have a really old map with a jillion things that need to be updated, but not deleted outright, and not ALL of those jillion things are necessary for removal or updating or what have you, but certain kinds are. I'll provide an example, and I'll link to the BBWaffleBox code that UArchitect wrote for me ages ago, but I never really used too much until somewhat recently. I've made some AWESOME quick batch operations with this thing.
Here's the full BBWaffleBox package: http://www.mediafire.com/download.php?rl6fmag0vnt2g9x
To install, extract everything to \UT\System\. There should be a Waffle icon, and that goes in \System\editorres\ if you didn't put it there already. Now open UnrealTournament.ini and add BBWaffleBox to your EditPackages list somewhere at the end. Open UED and you should see the icon! YOU'RE ON YOUR WAY TO GREATNESS!
Now go to Object >> Brushbuilder >> BBWaffleBox and edit the script!
If you already know how to make your own packages and just want to see the code UArch gave me initially to set up some serious brushbuildin', then look no further:
[spoiler]
Code: Select all
//=============================================================================
// BBWafflebox.
//=============================================================================
class BBWafflebox expands BrushBuilder;
var transient brush _bbrush; // builder brush
var transient levelinfo _level; // current levelinfo actor
var transient editorengine _eengine;
var transient camera _camera;
var transient object _input;
/////////////////////////////////////////////////////////////
// SET UP REQUIRED VARS
/////////////////////////////////////////////////////////////
function bool Setup() {
local int i;
_bbrush=none;
_level=none;
_eengine=none;
_camera=none;
_input=none;
for(i=0; (i<100 && _level==none); i++)
setpropertytext("_level","levelinfo'levelinfo"$i$"'");
if(_level==none) return badparameters("failed to find levelinfo");
else log(getclassname(self.class)$" found levelinfo: "$_level);
for(i=0; (i<100 && _bbrush==none); i++)
setpropertytext("_bbrush","brush'brush"$i$"'");
if(_bbrush==none) return badparameters("failed to find builder brush");
else log(getclassname(self.class)$" found builder brush: "$_bbrush);
for(i=0; (i<100 && _eengine==none); i++)
setpropertytext("_eengine","editorengine'editorengine"$i$"'");
if(_eengine==none) return badparameters("failed to find editor engine");
else log(getclassname(self.class)$" found editor engine: "$_eengine);
return true;
}
// this function gets called when the builder brush button is clicked
event bool Build()
{
Setup(); // set up initial vars and stuff for more advanced brushbuildin'
Waffle();
return BadParameters("YOUR FORGOT THE CODE YOU DUMPASS!");
}
/////////////////////////////////////////////////////////////
// PUT YOUR DAMN BROKEN ASS CODE HERE
/////////////////////////////////////////////////////////////
function Waffle()
{
/* // BEST BRUSHBUILDER YET
local Candle C;
local EXUFlare E;
foreach _level.Allactors(class'Candle', C)
{
if(C.DrawScale == 0.3)
{
E = _level.Spawn(Class'EXUFlare',,, c.location + vect(0,0,3.76) );
if(E!=none)
{
E.Group = 'CandleClusterFlicker3';
E.DrawScale = 0.15;
E.Texture = Texture'GenFX.LensFlar.flare7';
E.bFlickering = True;
E.bFlickerDrawScale = True;
E.DSMax = 0.15;
E.DSMin = 0.125;
E.SGMax = 1.0;
E.SGMin = 0.75;
}
}
}
*/
}
/////////////////////////////////////////////////////////////
// UTILITY FUNCTIONS
/////////////////////////////////////////////////////////////
function string GetClassName(class<object> c)
{
local string s;
if(c==none) return "";
s = string(c);
return right(s,instr(s,"."));
}
function string GetPackageName(class<object> c)
{
local string s;
if(c==none) return "";
return string(c.outer);
}
static final function float acos (float a)
{
if(a>1||a<-1) return 0;
if(a==0) return pi/2;
a = atan(sqrt(1.0-square(a))/a);
if(a<0) a += pi;
return a;
}
static final function float asin(float a)
{
if(a>1||a<-1) return 0;
if(a==1) return pi/2;
if(a==-1) return -pi/2;
return atan(a/sqrt(1-square(a)));
}
I honestly haven't the foggiest clue what those utility functions are for, but SOMEONE HERE PROBABLY DOES! THEY PROBABLY HOLD THE SECRET TO COLD FUSION OR SOMETHING! UARCHITECT IS A GENIUS AND CLEARLY HAS HARNESSED THE POWER OF BRUSHBUILDERS TO ENHANCE HIS LIFE IN WAYS MERE MORTALS CAN ONLY BEGIN TO COMPREHEND.
Note that in that code, I have included my example (which I just used tonight). I'll go into detail on that now, as well as a couple other examples.
In an EXU2 map, Highlands specifically for anyone familiar, I had tons, and I mean TONS, of candles. All over the map. Clumped together in threes, with one big candle, one medium candle, and one little candle. All the same "Candle" actor to boot. I had them grouped properly, so all the big candles were in a group and etc, and each candle also had a corona. I decided that I'd rather replace the corona lights with EXUFlare classes, which are much more flexible and able to be set up to look more like an actual flickering candle without being super bright at long range.
BUT HOW AM I SUPPOSED TO REPLACE OVER 180 CANDLES' WORTH OF FLARES WHEN THEY'RE ALL OVER THE MAP, DIFFERENTLY SIZED, AND NOT EVEN IN ALL THE SAME RELATIVE POSITIONS? Well, my friend, I'm glad you asked! BRUSHBUILDERS!
Let's go through this line-by-line:
Code: Select all
/////////////////////////////////////////////////////////////
// PUT YOUR DAMN BROKEN ASS CODE HERE
/////////////////////////////////////////////////////////////
function Waffle()
{
/* // BEST BRUSHBUILDER YET
local Candle C;
local EXUFlare E;
foreach _level.Allactors(class'Candle', C)
{
if(C.DrawScale == 0.3)
{
E = _level.Spawn(Class'EXUFlare',,, c.location + vect(0,0,3.76) );
if(E!=none)
{
E.Group = 'CandleClusterFlicker3';
E.DrawScale = 0.15;
E.Texture = Texture'GenFX.LensFlar.flare7';
E.bFlickering = True;
E.bFlickerDrawScale = True;
E.DSMax = 0.15;
E.DSMin = 0.125;
E.SGMax = 1.0;
E.SGMin = 0.75;
}
}
}
*/
}
local Candle C; allows me to iterate through all the actors in the level. Since UArch already set up _level for us in Setup(), we call _level.foreach(allactors). Since foreach(allactors) is a native function defined in Actor, not Object, we can't call just foreach(allactors) in an Object subclass. So we instead hook into the LevelInfo, an Actor, and reference that instead. Bam! We are now able to iterate through all the actors in the level FROM an object class, the brushbuilder! Are you amazed yet? You should be.
local EXUFlare E; sets me up for spawning an EXUFlare E at each Candle C I find in the level. But wait, surely you don't mean EVERY candle, right? They're all differently sized, and you can't just use one EXUFlare class to fit all three sizes for all 180+ candles! EXACTLY, MY FRIEND. EXACTLY. WE'VE GOT THIS COVERED.
foreach _level.allactors(class'Candle', C) as mentioned above, we're iteratin' now! Any candle that gets found will be stored and accessible as C.
if( C.DrawScale == 0.3 ) Heeeeere's where things get interesting! I have set up a condition to ONLY work with Candle actors that fit this specific parameter. Since it's extremely unlikely that any other Candle actors in the map that are set to DrawScale=0.03 will NOT be the kind I'm looking to modify, I leave it at this. But I could always append an && Group=='NameOfCandleGroup1' to be absolutely sure I ONLY get the Candle classes I want (since, like I said, they're all grouped. Use groups, people, they are absolutely indispensable for editing large groups of the same kind of actors all at once).
In this case, Candles that are 0.03 are the little ones, with bigger ones being 0.05 and the biggest being 0.075. That's mostly irrelevant, though, because what comes NEXT in the list is where I do the magic!
E = _level.Spawn(Class'EXUFlare',,, c.location + vect(0,0,3.76) ); allows me to spawn an EXUFlare EXACTLY where I want it, right at the base of the candle's flame, which is located ~3.76 units above the candle's local origin. I gathered this by subtracting Candle.Location.Z from EXUFlare.Location.Z from the first EXUFlare I placed in the level and configured as a test. (I also did two other test flares for the other two candle sizes, naturally.)
Now, if(E!=none), we set up E, the EXUFlare that just spawned at C.Location + 3.67 z-axis units! You can see I set the size, group, some booleans, and some floats to get it to look just like my test flare. That's basically it!
I compiled the code, then simply clicked the Waffle icon. Because I was too lazy to remove UArch's error message, I was berated for "forgetting the code" and, voila, ALL MY SMALL CANDLES HAD FLARES ON THEM TO REPLACE THE CORONAS. JUST. LIKE. THAT.
I edited the code to correspond to the other two sizes, adjusting the E.whatever variables as needed, then built again and a third time until ALL the Candle classes had an EXUFlare that was appropriate for its size. And since I assigned group labels, all my EXUFlare classes for each size are all mass-selectable via the Group menu so I can make further global changes without having to manually select each one!!! HOLY BALLS!!!!! TIMESAVINGS OUT THE ASS!
I also noticed I had a whole bunch of coronas on some other candles that were never grouped. There were tons of them, and since corona lights are naturally quite tiny, I really did not feel like manually selecting all of them. IN COMES THE BRUSHBUILDER!
Code: Select all
function Waffle()
{
local Light L;
foreach _level.Allactors(class'Light', L)
{
if( L.DrawScale == 0.025 && L.bCorona==True && L.Skin==Texture'GenFX.LensFlar.flare7' )
L.Destroy(); // Wow! We killed those bastards! And not a single other light that shouldn't have been nuked!!
}
}
Compile. Click. DONE. Coronas DESTROYED!
You can very easily make a Brushbuilder that converts actors into other actors as well. If you don't want to go through the .t3d method, you can basically get an actor class, spawn another actor at its same location and set its parameters as necessary, then destroy the original. Or something else like that! You can also do mass edits to actors, like when I had a billion EXUSpawnPoints with SpawnEffectClass set to UnrealShare.SpriteBallExplosion, an obsolete Effects class, I simply got every single EXUSpawnPoint in the level which used that as its SpawnEffectClass, then overrode it so SpawnEffectClass=Class'EXU.EXUOldSpriteBallExplosion'; and DONE! INSTANT CONVERSION OF HUNDREDS OF ACTORS. ONE CLICK. DONE. NO NONSENSE!!
If you don't know anything about UScript, well, great time to learn! But if you just have specific questions on how to make a quickie brushbuilder to do X, post your questions here! Wow! BRUSHBUILDERS ARE A SHINING BEACON OF HOPE IN THIS DARK WORLD WE INHABIT
A WORD OF WARNING: IF YOU DELETE ACTORS...
...be SURE you comment out the code in your brushbuilder that referenced the deleted class if you're going to remove it from your code package once you're done removing it from your level! Otherwise, if you save the brushbuilder's .u file with references to nonexistent classes, your brushbuilder won't work and it won't be editable anymore! That is the ultimate travesty. An uneditable, nonfunctional brushbuilder. Don't let it happen to you. Comment out your function code once you're done with it, then compile and save the package! Or don't save it at all if you're just doing a quickie one-off operation! Though it's always handy to keep records of what you did for future reference.
If you DO mess up like I did a few times, you just have to recreate the missing classes in the code package you edited, recompile it, then reload the brushbuilder package and comment out the code, save it, then re-delete the classes and recompile the code package so that the references are scrubbed. GOOD LUCK FRIENDS, MAY THE POWER OF BRUSHBUILDERS IMBUE YOUR LIVES WITH PROSPERITY AND JOY