Overview Package Class Source Class tree Glossary
previous class      next class frames      no frames

U2.TextureHelperGeneric


00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
//=============================================================================
// TextureHelperGeneric.uc
// $Author: Mfox $
// $Date: 12/15/02 6:43p $
// $Revision: 31 $
//=============================================================================

class TextureHelperGeneric extends LegendObjectComponent
	config(TextureSounds)
	abstract;

/*=============================================================================
TextureHelperGeneric:

Provides support for texture specific sounds and effects when a Pawn moves
(runs, walks, strafes, lands) on a surface.

Each sound is played with slightly randomized volume and pitch in order to
vary each sound somewhat for increased realism.

Sounds and impact effects are specified through the properties associated
with this class (see below). These can be edited either in this (or a
derived) .uc file, or through the UnrealEd properties editor. Sounds and
Effects are specified through the TextureEffects properties. See the
EffectInfoT structure definition below for a description of each of the
fields.

If a new sound is added, it should be #exec'd in using the same format as
is used for the sounds used in this class (see below). These should be 
placed near the top of the file for the class which uses these sounds.

It is possible that the per-sound Volume and Pitch fields may be removed.
At present, the Odds fields are not used and may also be removed.

The base volume and pitch multipliers can be used to control the overall
volume and pitch for an entire TextureHelperGeneric class. For example,
for a large creature, you might want to use the default sounds with a
slightly higher volume and slightly lower pitch.

For the best results, the actual sounds themselves should either be 
modified outside of the Unreal engine (e.g. with SoundForge) or replaced
with more suitable sounds. 

The identical sounds are used for all running and walking (with/without
strafing and/or attacking) movements. If the Pawn's speed is less than
RunningSoundSpeed, the volume of the sound is reduced by 
WalkVolumeMultiplier. The values of PitchVariancePercent and 
VolumeVariancePercent can be used to control how much the pitch and volume
is randomly varied for each sound before playing it. Setting these to +/- 
30% and 50% respectively seems to work fairly well.

Unless the defaults for this class are changed, the properties for this 
class will not normally be edited. Instead, subclass this class and make
any necessary changes to the subclass. Set the TextureHelperClass field
for Pawn classes which should use the new subclass to the name of the new
subclass (currently all PCs and NPCs have their own TextureHelperGeneric
which overrides nothing for now, except for the base pitch and/or volume
in the case of female or lighter/heavier characters so all PC/NPC texture
specific events can be placed in these).

For example, to have specific texture effects/sounds for the a new XXX Pawn
class:

1) Create a class called something like XXXTextureHelper. 

2) Either edit the default properties section or open up UnrealEd and glom 
in the damage types which you want to handle/override. IMO, the easiest way
to do this, at least to get things set up is to cut and paste the default 
TextureEffects properties for this class into the new class then edit things 
as needed and rebuild the package which the new class resides in. For small 
tweaks, using UnrealEd may be your best bet.

3) Set the TextureHelperClass for the XXX (in the XXX properties) to
XXXTextureHelper.

Note that the TextureHelperGeneric is accessed through the Pawn's 
AssetsHelperClass which specifies the AssetsHelperGeneric class to use. 
The AssetsHelperGeneric class is basically a wrapper for the 
TextureHelperGeneric class(es) (and it may wrap other assets-tracking
classes in the near future.

============================================================================
Level Design Issues

See TextureTypes.uc for information on setting up texture types which are
used below to determine the type of sound to use.

Note that if a texture isn't recognized, the Stone sound is played by
default. TextureType = TT_None can be used to disable sound for specific
textures.

============================================================================
Additional Notes

Groups of similar sounds (e.g. 4 run on wood sounds) can be given odds in
the properites, but these aren't used at present (each sound is just as 
likely to be used as the others).

Some maps can result in Texture=None because of a bug in the tracing code.
For now, when this happens, the stone sound is played by default.

There is a nasty UnrealEd bug. Properties which are structs that contain 
other structs can crash the editor and/or don't work properly.
(!!MWP (mdf) tbd: is this fixed now?)

At present, every sound can have its own effect. In many cases (e.g. for the
4 runwalk sounds), a single effect per texture would probably due, and if 
it wasn't for the above bug, this could be done. This is basically a space
optimization though, and probably won't get done in the near future.

One advantage of the way things are set up at present is that it is 
possible to have different effects for running/walking vs landing.

============================================================================
*/

// walking sounds
#exec OBJ LOAD FILE=..\Sounds\U2A.uax

const TraceTextureFudgeFactor = 128.0;

struct EffectInfoT
{
	var() Material.ETextureType	TextureType;						// texture on which sound plays
	var() string				SoundStr;   						// name of actual sound to play
	var() float					Volume;								// Volume to use
	var() float					Pitch;								// Pitch to use
// Disabling Odds as they aren't currently used and cause (somehow) nothing past element 85 to be read in.
//	var() float					Odds;								// Odds of sound being used (within a group)
	var() string				EffectName;							// if set, name of class to spawn along with sound
};

var(U2Textures)	config EffectInfoT	TextureEffects[ 128 ];			// texture-specific sounds -- assumes that texturetypes are compacted (no gaps) and sorted
var(U2Textures)	config float		BasePitch;						// pitch multiplier
var(U2Textures)	config float		BaseVolume;						// volume multiplier (for NPCs -- player scaled down)
var(U2Textures)	config float		PlaySoundRadius;				// radius to use with PlaySound
var(U2Textures)	config float		PitchVariancePercent;			// percentage to vary final pitch by +/-
var(U2Textures)	config float		VolumeVariancePercent;			// percentage to vary final volume by +/-
var(U2Textures)	config Material.ETextureType DefaultTextureType;	// default texture type (if none hit)

var float MakeNoiseVolumeMultiplier;								// amount to scale volume by for use with MakeNoise
var float RunningSoundSpeed;										// below this speed walking sounds used (lower volume)
var float WalkVolumeMultiplier;										// multiplier for volume when walking vs running

//-----------------------------------------------------------------------------

static function MaybeMakeNoise( U2Pawn U2P, float SelectedVolume )
{
	SelectedVolume = FClamp( SelectedVolume*default.MakeNoiseVolumeMultiplier, 0.0, 1.0 );
	//U2P.DMTNS( "MakeNoise: " $ SelectedVolume );
	U2P.MakeNoise( SelectedVolume );
}

//-----------------------------------------------------------------------------

static function DoTextureEffect( U2Pawn U2P, Sound SelectedSound, float SelectedVolume, float SelectedPitch, class<Actor> EffectC )
{
	local float SelectedRadius;
	
	MaybeMakeNoise( U2P, SelectedVolume );

	SelectedRadius = default.PlaySoundRadius;
	if( U2P.bIsRealPlayer )
	{
		SelectedVolume *= 0.33;
		SelectedRadius *= 0.33;
	}	
	
	U2P.PlayOwnedSound( SelectedSound, SLOT_Interact, SelectedVolume, false, default.PlaySoundRadius, SelectedPitch );

	if( EffectC != None )
		U2P.Spawn( EffectC,,, U2P.Location - vect(0,0,1)*U2P.CollisionHeight );
}

//-----------------------------------------------------------------------------

static function string GetTextureString( Material.ETextureType TextureType )
{
	return EnumStr( enum'ETextureType', TextureType );
}

//-----------------------------------------------------------------------------

static function TextureError( U2Pawn U2P, coerce string strMsg, Material.ETextureType TextureType )
{
	local string strTexture;

	strTexture	= GetTextureString( TextureType );

	U2P.DM( "WARNING: TextureError (" $ strMsg $ ") Class: " $ default.Class.Name $ " TextureType: " $ strTexture );
}

//-----------------------------------------------------------------------------
// SelectEffect
//
// TBD: use EffectInfoT.Odds to weight likelihood of each sound being played.
// (See code in grunt.uc PickAnim for this.)
//
// Note (mdf): scanning the table each time this function is called is
// horrendously inefficient and adds significant overhead. e.g., with 20
// araknids with fast animations + 2 notifies per animation I was seeing
// over 5ms / frame. By caching this operation so that it is only done
// when	the sound group or surface changes most of this overhead is 
// eliminated.
//
// Note that in addition to ~5.2ms due to the old version of this function
// there is ~0.4ms due to playing the sound, ~0.1ms due to the trace to
// determine the texture and ~0.2ms due to the calls to DynamicLoadObject
// for the sound (this could be eliminated by storing the sounds themselves
// in the context pawn instead of the sound table indices). The overhead for
// loading and spawning the effect is probably similar but we don't use 
// effects on most surfaces (so far just water). This leaves about another
// 0.7ms unaccounted for (e.g. if the PlayMovementSound is disabled 
// completely). Again, this is/was for 20 light araknids in a 4kx4kx4k CoD
// this will vary greatly with the number of NPCs and the actual level.

static function int SelectEffect( U2Pawn U2P, Material.ETextureType TextureType )
{
	local int SoundIndex;
	local int NumMatches;

	//U2P.DM( "SelectEffect" );
	// yet another exercise in caching
	if( default.Class != U2P.LastSoundClass || TextureType != U2P.LastTextureType )
	{
		//U2P.DM( "  caching candidates" );

		// scan to matching group/texture type (not guaranteed to be in the list)
		SoundIndex=0;
		while( SoundIndex < ArrayCount(default.TextureEffects) && default.TextureEffects[ SoundIndex ].TextureType != TextureType )
			 SoundIndex++;

		//U2P.DM( "  candidates start at " $ SoundIndex );

		// copy matching indices
		NumMatches = 0;
		while( SoundIndex < ArrayCount(default.TextureEffects) &&
			   NumMatches < ArrayCount(U2P.TextureSoundIndices) && 
			   default.TextureEffects[ SoundIndex ].TextureType == TextureType )
		{
			//U2P.DM( "    adding #" $ SoundIndex );
		 	U2P.TextureSoundIndices[ NumMatches++ ] = SoundIndex++;
		}

		U2P.NumTextureSounds = NumMatches;
		U2P.LastSoundClass = default.Class;
		U2P.LastTextureType = TextureType;

		if( U2P.NumTextureSounds <= 0 )
			TextureError( U2P, "SelectEffect", TextureType );
	}

	//U2P.DM( "  U2P.NumTextureSounds " $ U2P.NumTextureSounds );

	// pick one of the choices randomly (odds not used)
	if( U2P.NumTextureSounds > 0 )
	{
		SoundIndex = Rand( U2P.NumTextureSounds );
		//U2P.DM( "  returning entry #" $ SoundIndex $ ": " $ U2P.TextureSoundIndices[ SoundIndex ] );
		return U2P.TextureSoundIndices[ SoundIndex ];
	}

	return -1;
}	

//-----------------------------------------------------------------------------

static function bool GetTextureParameters( U2Pawn U2P, Material.ETextureType TextureType, out Sound SoundOut, out float VolumeOut, out float PitchOut, out class<Actor> EffectOut )
{
	local int SoundIndex;

	SoundIndex = SelectEffect( U2P, TextureType );
	if( SoundIndex != -1)
	{
		if( default.TextureEffects[ SoundIndex ].SoundStr != "" )
		{
			SoundOut = Sound( DynamicLoadObject( default.TextureEffects[ SoundIndex ].SoundStr, class'Sound' ) );
			if( SoundOut == None )
				TextureError( U2P, "couldn't load sound: " $ default.TextureEffects[ SoundIndex ].SoundStr, TextureType );
		}
		else
			TextureError( U2P, "no sound", TextureType );

		VolumeOut	= default.TextureEffects[ SoundIndex ].Volume * default.BaseVolume;
		PitchOut	= default.TextureEffects[ SoundIndex ].Pitch * default.BasePitch;

		if( default.TextureEffects[SoundIndex].EffectName != "" )
		{
			EffectOut = class<Actor>( DynamicLoadObject( default.TextureEffects[SoundIndex].EffectName, class'Class' ) );

			if( EffectOut == None )
				TextureError( U2P, "couldn't load effect: " $ default.TextureEffects[SoundIndex].EffectName, TextureType );
		}

		return true;
	}

	return false;
}

//-----------------------------------------------------------------------------
// DetermineTextureEffect:
//
// Look up the effects which matches the given Surface and apply these (play 
// the sound, spawn the effect). If the surface is None then uses 
// DefaultTextureType.
//
// The final volume (before it is randomly varied) is scaled by 
// VolumeMultiplier.

static function DetermineTextureEffect( U2Pawn U2P, Material.ETextureType TextureType, float VolumeMultiplier )
{
	local Sound SelectedSound;
	local float SelectedVolume;
	local float SelectedPitch;
	local class<Actor> EffectC;

	// tbd: don't play a sound if there is no surface (otherwise agent NPCs currently "walk" while flying ==> sounds
	//else
	//{
	//	// pathological case -- occasionally the hit surface is "none"
	//	TextureType = default.DefaultTextureType;
	//}

	if( TextureType == TT_None || TextureType == TT_Default )
		TextureType = default.DefaultTextureType; // use default for untextured surfaces
		
	if( TextureType != TT_None )
	{
		if( GetTextureParameters( U2P, TextureType, SelectedSound, SelectedVolume, SelectedPitch, EffectC ) )
		{
			SelectedVolume *= VolumeMultiplier;
	
			// vary the pitch a bit
			SelectedPitch = class'Util'.static.PerturbFloatPercent( SelectedPitch, default.PitchVariancePercent );

			// vary the volume a bit
			SelectedVolume = class'Util'.static.PerturbFloatPercent( SelectedVolume, default.VolumeVariancePercent );

			//U2P.DM( "DeterminTextureEffect - Surface: " $ GetTextureString(TextureType) $ " S:" $ SelectedSound $ " V:" $ SelectedVolume $ " U2P:" $ SelectedPitch $ " E:" $ EffectC );
			
			DoTextureEffect( U2P, SelectedSound, SelectedVolume, SelectedPitch, EffectC );
		}
		else
		{
			TextureError( U2P, "DetermineTextureEffect", TextureType );
		}
	}
}

//-----------------------------------------------------------------------------

static function bool CheckForWater( U2Pawn U2P )
{
	local int ii;

	for( ii=0; ii<U2P.Touching.Length; ii++ )
	{
		// Check if a waterzone or fluidsurface is being touched
		if( (PhysicsVolume(U2P.Touching[ii]) != None && PhysicsVolume(U2P.Touching[ii]).bWaterVolume) ||
			FluidSurfaceInfo(U2P.Touching[ii]) != None )
		{
			return true;
		}
	}
	
	return false;
}

//-----------------------------------------------------------------------------

static function Material.ETextureType GetTextureType( U2Pawn U2P )
{
	local Material Surface;

	if( U2P.Physics == PHYS_Ladder )
		return TT_Metal; //!!mdf-tbd: ladder hack
	
	if( CheckForWater( U2P ) )
		return TT_Water;
	
	// identify texture under actor -- need to be able to specify that actor extents should be used
	// (most general approach is pbly to pass in extents).
	Surface = U2P.TraceTexture( U2P.Location + vect(0,0,-1) * (U2P.CollisionHeight + TraceTextureFudgeFactor) );
	if( Surface != None )
		return Surface.TextureType;
	
	return TT_None;
}

//-----------------------------------------------------------------------------
// NOTE: ImpactSpeed is usually negative (falling).

static function HandleLandingOnTexture( U2Pawn U2P, float ImpactSpeed )
{
	local float VolumeMultiplier;
 	
	// landed volume increases with impact velocity
	VolumeMultiplier = Abs(ImpactSpeed/U2P.JumpZ);
	VolumeMultiplier *= U2P.Mass / class'Actor'.default.Mass;
	
	DetermineTextureEffect( U2P, GetTextureType( U2P ), VolumeMultiplier );
}

//-----------------------------------------------------------------------------

static function HandleMovingOnTexture( U2Pawn U2P )
{
	local float VolumeMultiplier;

	// if walking, sound is quieter
	VolumeMultiplier = 1.0;
	if( U2P.Physics != PHYS_Ladder )
	{
		if( VSize(U2P.Velocity) < default.RunningSoundSpeed )
			VolumeMultiplier = default.WalkVolumeMultiplier;
	}
	VolumeMultiplier *= U2P.Mass / class'Actor'.default.Mass;
	
	DetermineTextureEffect( U2P, GetTextureType( U2P ), VolumeMultiplier );
}

//-----------------------------------------------------------------------------

defaultproperties
{
	BasePitch=1.000000
	BaseVolume=1.000000
	PlaySoundRadius=75.000000
	PitchVariancePercent=10.000000
	VolumeVariancePercent=10.000000
	DefaultTextureType=TT_Stone
	MakeNoiseVolumeMultiplier=0.350000
	RunningSoundSpeed=200.000000
	WalkVolumeMultiplier=0.250000
}

Overview Package Class Source Class tree Glossary
previous class      next class frames      no frames
Class file time: sk 3-1-2016 10:38:42.000 - Creation time: sk 3-1-2016 10:48:42.328 - Created with UnCodeX