UDN
Search public documentation:

PhysicalMaterialSystem
日本語訳
中国翻译
한국어

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

Physical Materials

Document Summary: This page details the Physical Material system and gives some examples on how to create material based effects (e.g. impact effects, footstep sounds) preface.

Document Changelog: created preface.

Overview

This page is broken down into the following sections:

-Overview of the PhysicalMaterial system.
-Detailing how to create a Hierarchy of Physical Materials.
-How to then use that Hierarchy for material based effects.
-Example Code of how to make material based footsteps work

Overview of Physical Material System

In UE3 you can attach a PhysicalMaterial to a Material or MaterialInstanceConstant. Attaching, as opposed to just having a material have additional properties, allows for you to reuse your PhysicalMaterialSystem on multiple Materials.

A quick example would be the following:

You have a gravelRoad Material which you have applied to the gravel road in the game. That gravelRoad Material has a PhysicalMaterial called gravel.

In that gravel PhysicalMaterial you have set the various footstep sounds that play based on the PawnType whom is doing the foot stepping.

When PawnTypeOne walks, it plays a sound from SoundCue A.

When PawnTypeTwo walks, it plays a sound from SoundCue B.

**Two physical materials on the same material are now supported on a single material. When a collision occurs the line check code finds the triangle hit, gets the texture UV at the hit location and looks up into a mask texture to determine if the hit pixel is black or white. The corresponding physical material will be returned.

Details for Programmers

Details:

Look at PhysicalMaterial.uc

Specifically:

var(PhysicalProperties) export editinline PhysicalMaterialPropertyBase PhysicalMaterialProperty;

PhysicalMaterialPropertyBase.uc has the comments you will want to read!

To get contextual footstep sounds and lay the framework for having other neat PhysicalMaterial based effects you would make a class:

class MyGamePhysicalMaterialProperty extends PhysicalMaterialPropertyBase
collapsecategories
editinlinenew
hidecategories(Object);


var(FootSteps) editinline MyGamePhysicalMaterialFootSteps MyGamePhysicalMaterialFootSteps;

and then in class MyGamePhysicalMaterialFootSteps you have:

var() SoundCue ProtagonistFootSteps;

var() SoundCue NPCTypeOneFootSteps;

var() SoundCue NPCTypeTwoFootSteps;
etc

So now when a Pawn steps (either through AnimNotify_Footstep or some other method) you can call eventPlayFootStepSound which will then send that event to the correct pawn. Now that pawn can determine what sound to play based on its type and the physical material!

e.g. NPC Ogre is walking along the gravel road. When it steps and eventPlayFootStepSound has been called then trace down, find the physical material, get the SoundCue? based on the pawn type and play it.

NOTE: currently you will have to do a trace down to get the physical material. We are probably going to update eventPlayFootStepSound to pass a physical material as it will be more efficient to do the trace straight in c++ land and that this is a feature that most licensees want in their games. Contextual Footstep sounds are nice!

Obviously with the PhysicalMaterial system and a framework of properties, like the above, you can do all kinds of really awesome contextual functionality (e.g. impact sounds, hit effects, decals, footsteps, etc.)

Hierarchy of Physical Materials.

Once your programming staff has created the specific game classes above, your content team can start making Hierarchies of Physical Materials and start populating them with the effects your game design demands.

Details for Content Creators

The PhysicalMaterial has a var(Parent)PhysicalMaterial Parent; This allows you to create hierarchies of physical materials and thereby (in our example case) hierarchies of FootStep sounds.

So you can have:

Default
^
|
Clay
^
|
Wet Clay

So now your content team can be really specific in the physical materials they have in the game world. And when you want to have a Wet Clay footstep sound you can. If you don't have a Wet Clay footstep sound then you can look up the hierarchy until you find one or end up at the Default which should have your default footstep sound.

Using the Hierarchy

So now that you have both code support and a paramaterized Physical Material Hierarchy you can now assign Physical Materials to Materials! Once you have assigned them you should hear them playing in the game.

Content Suggestions

When making footsteps (to continue our example) you should have your sound team make SoundCues with randomized footsteps in both the source sound and then the pitch and attenuation. This leads to a more believable foot step sound. Additionally it provides the correct abstraction to the Physical Material system.

Example code.

/**
*  Event from c++ land that tells us to play a footstep
**/
event PlayFootStepSound(int FootDown)
{
   local vector PawnLoc;
   local float CurrHeight;

   local Actor TraceActor;

   local vector out_HitLocation;
   local vector out_HitNormal;
   local vector TraceDest;
   local vector TraceStart;
   local vector TraceExtent;
   local TraceHitInfo HitInfo;

   // kk here we need to do a tracez0r down down into the ground baby!
   // NOTE: this will eventually be moved to c++ land
   PawnLoc = self.Location;
   CurrHeight = self.GetCollisionHeight();

   TraceStart = PawnLoc;
   TraceDest = PawnLoc - ( Vect(0, 0, 1 ) * CurrHeight ) - Vect(0, 0, 15 );

   // trace down and see what we are standing on
   TraceActor = Trace( out_HitLocation, out_HitNormal, TraceDest, TraceStart, false, TraceExtent, HitInfo, true );

   //DrawPersistentDebugLine( TraceStart, TraceDest, 255, 0, 0 );

   if( TraceActor != none )
   {
      // play the actual sound now
      ActuallyPlayFootStepSound( FootDown, HitInfo );
      MakeNoise( 0.1f, 'NOISETYPE_FootStep' );
   }
   // otherwise we hit nothing and are in the air
   else
   {
   //   Log( " We are in the air" );
   }
   
}

/**
* This will figure out which material we are standing on and then play the
* correct footstep sound.
* It looks up the PhysicalMaterial tree until it finds a valid effect to Spawn.
* If it does not find one then it defaults the effect coded in to the .uc file
* for that specific weapon
**/
function ActuallyPlayFootStepSound( int FootDown, TraceHitInfo HitInfo )
{
   local PhysicalMaterial ParentPhysMaterial;
   local SoundCue SC;
   local SoundCue FootStepSound;

   // XO5 HAX0R
   // if we are in the rain then play water footsteps
   // NOTE: right now terrain dows not return material for traces so we need to do this
   //if( IsTimerActive( 'DropRainDropsOnToPawn' ) == TRUE )
   if( bShouldPlayWetFootSteps == TRUE )
   {
      Self.PlaySound( SoundCue'FootSteps.SoundCues.Footsteps_Marcus_Water', FALSE, TRUE );
      return;
   }


   // if we do not have a PhysicalMaterial 
   if( HitInfo.PhysMaterial != none )
   {
      FootStepSound = GetFootStepSound( HitInfo.PhysMaterial, FootDown );
   }
   else
   {
      // check to see if there is a material
      // if there is no material then we must use the default PS_DefaultImpactEffect
      if( HitInfo.Material != none )
      {
         FootStepSound = GetFootStepSound( HitInfo.Material.PhysMaterial, FootDown );
      }
      else
      {
         // always play the "Default footsteps sounds" which is the dirt step
         Self.PlaySound( SoundCue'FootSteps.SoundCues.Footsteps_Marcus_Dirt', FALSE, TRUE );
         return;
      }
   }


   // so here we need to look up the "hierarchy" if we have a none in the attribute we want
   // and we probably want to maybe have a code fall back to play some default
   // if the content doens't provide our hungry mouths with data

   // if we have no phys material
   if( HitInfo.PhysMaterial != none )
   {
      ParentPhysMaterial = HitInfo.PhysMaterial.Parent;
   }
   // set the parent phys material here
   else
   {
      // check to see if the material has a phys material
      if( HitInfo.Material.PhysMaterial != none )
      {
         ParentPhysMaterial = HitInfo.Material.PhysMaterial.Parent;   
      }
      else
      {
         ParentPhysMaterial = none;
      }
   }


   // this will walk the tree until our parent is null or we have a footstep sound
   // at which point we will break out (which is basically an exception case)
   // but there are no exceptions in .uc land
   while( ( FootStepSound != none )
      && ( ParentPhysMaterial != none )
      )
   {
      // look at our parent's data
      FootStepSound = GetFootStepSound( ParentPhysMaterial, FootDown );
      ParentPhysMaterial = ParentPhysMaterial.Parent;
   }


   // use the material based effect
   if( FootStepSound != none )
   {
      //Log( " Playing Sound: " $ FootStepSound );
      SC = FootStepSound;
   }
   // do default behavior
   else
   {
      //Log( " Playing Default Sound" );
      SC = SoundCue'FootSteps.SoundCues.Footsteps_Marcus_Dirt';
   }

   Self.PlaySound( SC, FALSE, TRUE );
}


/**
* Looks to see if the Sound on the current PhysicalMaterial is valid.
**/
function SoundCue GetFootStepSound( PhysicalMaterial PMaterial, int FootDown )
{
   local SoundCue Retval;

   Retval = none;

   // our Specific properties exists now we need to call our function to get out
   // the specificProperty
   if( ( none != PMaterial )
      && ( none != PMaterial.PhysicalMaterialProperty )
      && ( none != WarPhysicalMaterialProperty(PMaterial.PhysicalMaterialProperty) )
      && ( none != WarPhysicalMaterialProperty(PMaterial.PhysicalMaterialProperty).WarPhysicalMaterialFootSteps )
      )
   {
      Retval = GetSpecificFootStepSound( WarPhysicalMaterialProperty(PMaterial.PhysicalMaterialProperty).WarPhysicalMaterialFootSteps, FootDown );
   }
   else
   {
      Retval = none;
   }

   return Retval;
}


/**
* Each object is going to override this and return the data based off the passed in
* Object.  (e.g.  GetSpecificFootStepSound called on a marcus will look up the
* marcus footstep sound, called on a locust will look up the locust sound)
**/
function SoundCue GetSpecificFootStepSound( WarPhysicalMaterialFootSteps FootStepSounds, int FootDown )
{
   local SoundCue Retval;

   // do possible computation or what not.
   Retval = None;

   return Retval;
}


e.g. in class BigOgre


function SoundCue GetSpecificFootStepSound( MyGamePhysicalMaterialFootSteps FootStepSounds, int FootDown )
{
   local SoundCue Retval;

   // do possible computation or what not.
   Retval = SoundCue'FootSteps.SoundCues.Footsteps_Player_Dirt';  // or make this a config and store the value in an .ini for editing by non programmers

   return Retval;
}