c# - Unity Multiplayer Car : networking particles and sound -
i making unity game player controlling car , want game multiplayer. using standard assets' vehicule package car.
the structure of default car prefab controls separated between "user control" script handles user inputs, , "car controller" handles physics ( movements, acceleration, turning, steering etc).
you can find below sources of 2 scripts, modified carusercontrol.cs networking.
networking position not problem , works fine (i attached networktransform component car , networktransformchild wheels).
the thing car controller script handles particle effects when car steering (smoke generated , steering track appears), , sounds (acceleration, steering, skidding), , have absolutely no idea on how network that...
so right have multiplayer car movement, local car generates steering/skidding tracks, smoke, , sounds.
any idea on how it?
sources:
modified caruserinput.cs : added networkbehavior
instead of monobehavior
, added islocalplayer
check
using system; using unityengine; using unitystandardassets.crossplatforminput; using unityengine.networking; namespace unitystandardassets.vehicles.car { [requirecomponent(typeof (carcontroller))] public class carusercontrol : networkbehaviour { private carcontroller m_car; // car controller want use private void awake() { // car controller m_car = getcomponent<carcontroller>(); } private void fixedupdate() { if (!islocalplayer) return; // pass input car! float h = crossplatforminputmanager.getaxis("horizontal"); float v = crossplatforminputmanager.getaxis("vertical"); #if !mobile_input float handbrake = crossplatforminputmanager.getaxis("jump"); m_car.move(h, v, v, handbrake); #else m_car.move(h, v, v, 0f); #endif } } }
default carcontroller.cs:
using system; using unityengine; namespace unitystandardassets.vehicles.car { internal enum cardrivetype { frontwheeldrive, rearwheeldrive, fourwheeldrive } internal enum speedtype { mph, kph } public class carcontroller : monobehaviour { [serializefield] private cardrivetype m_cardrivetype = cardrivetype.fourwheeldrive; [serializefield] private wheelcollider[] m_wheelcolliders = new wheelcollider[4]; [serializefield] private gameobject[] m_wheelmeshes = new gameobject[4]; [serializefield] private wheeleffects[] m_wheeleffects = new wheeleffects[4]; [serializefield] private vector3 m_centreofmassoffset; [serializefield] private float m_maximumsteerangle; [range(0, 1)] [serializefield] private float m_steerhelper; // 0 raw physics , 1 car grip in direction facing [range(0, 1)] [serializefield] private float m_tractioncontrol; // 0 no traction control, 1 full interference [serializefield] private float m_fulltorqueoverallwheels; [serializefield] private float m_reversetorque; [serializefield] private float m_maxhandbraketorque; [serializefield] private float m_downforce = 100f; [serializefield] private speedtype m_speedtype; [serializefield] private float m_topspeed = 200; [serializefield] private static int noofgears = 5; [serializefield] private float m_revrangeboundary = 1f; [serializefield] private float m_sliplimit; [serializefield] private float m_braketorque; private quaternion[] m_wheelmeshlocalrotations; private vector3 m_prevpos, m_pos; private float m_steerangle; private int m_gearnum; private float m_gearfactor; private float m_oldrotation; private float m_currenttorque; private rigidbody m_rigidbody; private const float k_reversingthreshold = 0.01f; public bool skidding { get; private set; } public float brakeinput { get; private set; } public float currentsteerangle{ { return m_steerangle; }} public float currentspeed{ { return m_rigidbody.velocity.magnitude*2.23693629f; }} public float maxspeed{get { return m_topspeed; }} public float revs { get; private set; } public float accelinput { get; private set; } // use initialization private void start() { m_wheelmeshlocalrotations = new quaternion[4]; (int = 0; < 4; i++) { m_wheelmeshlocalrotations[i] = m_wheelmeshes[i].transform.localrotation; } m_wheelcolliders[0].attachedrigidbody.centerofmass = m_centreofmassoffset; m_maxhandbraketorque = float.maxvalue; m_rigidbody = getcomponent<rigidbody>(); m_currenttorque = m_fulltorqueoverallwheels - (m_tractioncontrol*m_fulltorqueoverallwheels); } private void gearchanging() { float f = mathf.abs(currentspeed/maxspeed); float upgearlimit = (1/(float) noofgears)*(m_gearnum + 1); float downgearlimit = (1/(float) noofgears)*m_gearnum; if (m_gearnum > 0 && f < downgearlimit) { m_gearnum--; } if (f > upgearlimit && (m_gearnum < (noofgears - 1))) { m_gearnum++; } } // simple function add curved bias towards 1 value in 0-1 range private static float curvefactor(float factor) { return 1 - (1 - factor)*(1 - factor); } // unclamped version of lerp, allow value exceed from-to range private static float ulerp(float from, float to, float value) { return (1.0f - value)*from + value*to; } private void calculategearfactor() { float f = (1/(float) noofgears); // gear factor normalised representation of current speed within current gear's range of speeds. // smooth towards 'target' gear factor, revs don't instantly snap or down when changing gear. var targetgearfactor = mathf.inverselerp(f*m_gearnum, f*(m_gearnum + 1), mathf.abs(currentspeed/maxspeed)); m_gearfactor = mathf.lerp(m_gearfactor, targetgearfactor, time.deltatime*5f); } private void calculaterevs() { // calculate engine revs (for display / sound) // (this done in retrospect - revs not used in force/power calculations) calculategearfactor(); var gearnumfactor = m_gearnum/(float) noofgears; var revsrangemin = ulerp(0f, m_revrangeboundary, curvefactor(gearnumfactor)); var revsrangemax = ulerp(m_revrangeboundary, 1f, gearnumfactor); revs = ulerp(revsrangemin, revsrangemax, m_gearfactor); } public void move(float steering, float accel, float footbrake, float handbrake) { (int = 0; < 4; i++) { quaternion quat; vector3 position; m_wheelcolliders[i].getworldpose(out position, out quat); m_wheelmeshes[i].transform.position = position; m_wheelmeshes[i].transform.rotation = quat; } //clamp input values steering = mathf.clamp(steering, -1, 1); accelinput = accel = mathf.clamp(accel, 0, 1); brakeinput = footbrake = -1*mathf.clamp(footbrake, -1, 0); handbrake = mathf.clamp(handbrake, 0, 1); //set steer on front wheels. //assuming wheels 0 , 1 front wheels. m_steerangle = steering*m_maximumsteerangle; m_wheelcolliders[0].steerangle = m_steerangle; m_wheelcolliders[1].steerangle = m_steerangle; steerhelper(); applydrive(accel, footbrake); capspeed(); //set handbrake. //assuming wheels 2 , 3 rear wheels. if (handbrake > 0f) { var hbtorque = handbrake*m_maxhandbraketorque; m_wheelcolliders[2].braketorque = hbtorque; m_wheelcolliders[3].braketorque = hbtorque; } calculaterevs(); gearchanging(); adddownforce(); checkforwheelspin(); tractioncontrol(); } private void capspeed() { float speed = m_rigidbody.velocity.magnitude; switch (m_speedtype) { case speedtype.mph: speed *= 2.23693629f; if (speed > m_topspeed) m_rigidbody.velocity = (m_topspeed/2.23693629f) * m_rigidbody.velocity.normalized; break; case speedtype.kph: speed *= 3.6f; if (speed > m_topspeed) m_rigidbody.velocity = (m_topspeed/3.6f) * m_rigidbody.velocity.normalized; break; } } private void applydrive(float accel, float footbrake) { float thrusttorque; switch (m_cardrivetype) { case cardrivetype.fourwheeldrive: thrusttorque = accel * (m_currenttorque / 4f); (int = 0; < 4; i++) { m_wheelcolliders[i].motortorque = thrusttorque; } break; case cardrivetype.frontwheeldrive: thrusttorque = accel * (m_currenttorque / 2f); m_wheelcolliders[0].motortorque = m_wheelcolliders[1].motortorque = thrusttorque; break; case cardrivetype.rearwheeldrive: thrusttorque = accel * (m_currenttorque / 2f); m_wheelcolliders[2].motortorque = m_wheelcolliders[3].motortorque = thrusttorque; break; } (int = 0; < 4; i++) { if (currentspeed > 5 && vector3.angle(transform.forward, m_rigidbody.velocity) < 50f) { m_wheelcolliders[i].braketorque = m_braketorque*footbrake; } else if (footbrake > 0) { m_wheelcolliders[i].braketorque = 0f; m_wheelcolliders[i].motortorque = -m_reversetorque*footbrake; } } } private void steerhelper() { (int = 0; < 4; i++) { wheelhit wheelhit; m_wheelcolliders[i].getgroundhit(out wheelhit); if (wheelhit.normal == vector3.zero) return; // wheels arent on ground dont realign rigidbody velocity } // if needed avoid gimbal lock problems make car shift direction if (mathf.abs(m_oldrotation - transform.eulerangles.y) < 10f) { var turnadjust = (transform.eulerangles.y - m_oldrotation) * m_steerhelper; quaternion velrotation = quaternion.angleaxis(turnadjust, vector3.up); m_rigidbody.velocity = velrotation * m_rigidbody.velocity; } m_oldrotation = transform.eulerangles.y; } // used add more grip in relation speed private void adddownforce() { m_wheelcolliders[0].attachedrigidbody.addforce(-transform.up*m_downforce* m_wheelcolliders[0].attachedrigidbody.velocity.magnitude); } // checks if wheels spinning , 3 things // 1) emits particles // 2) plays tiure skidding sounds // 3) leaves skidmarks on ground // these effects controlled through wheeleffects class private void checkforwheelspin() { // loop through wheels (int = 0; < 4; i++) { wheelhit wheelhit; m_wheelcolliders[i].getgroundhit(out wheelhit); // tire slipping above given threshhold if (mathf.abs(wheelhit.forwardslip) >= m_sliplimit || mathf.abs(wheelhit.sidewaysslip) >= m_sliplimit) { m_wheeleffects[i].emittyresmoke(); // avoiding 4 tires screeching @ same time // if can lead strange audio artefacts if (!anyskidsoundplaying()) { m_wheeleffects[i].playaudio(); } continue; } // if wasnt slipping stop audio if (m_wheeleffects[i].playingaudio) { m_wheeleffects[i].stopaudio(); } // end trail generation m_wheeleffects[i].endskidtrail(); } } // crude traction control reduces power wheel if car wheel spinning private void tractioncontrol() { wheelhit wheelhit; switch (m_cardrivetype) { case cardrivetype.fourwheeldrive: // loop through wheels (int = 0; < 4; i++) { m_wheelcolliders[i].getgroundhit(out wheelhit); adjusttorque(wheelhit.forwardslip); } break; case cardrivetype.rearwheeldrive: m_wheelcolliders[2].getgroundhit(out wheelhit); adjusttorque(wheelhit.forwardslip); m_wheelcolliders[3].getgroundhit(out wheelhit); adjusttorque(wheelhit.forwardslip); break; case cardrivetype.frontwheeldrive: m_wheelcolliders[0].getgroundhit(out wheelhit); adjusttorque(wheelhit.forwardslip); m_wheelcolliders[1].getgroundhit(out wheelhit); adjusttorque(wheelhit.forwardslip); break; } } private void adjusttorque(float forwardslip) { if (forwardslip >= m_sliplimit && m_currenttorque >= 0) { m_currenttorque -= 10 * m_tractioncontrol; } else { m_currenttorque += 10 * m_tractioncontrol; if (m_currenttorque > m_fulltorqueoverallwheels) { m_currenttorque = m_fulltorqueoverallwheels; } } } private bool anyskidsoundplaying() { (int = 0; < 4; i++) { if (m_wheeleffects[i].playingaudio) { return true; } } return false; } } }
you can sync states across server , clients.
inside carusercontrol
script, add field:
[syncvar] bool smokeon;
in update
loop:
if (!isserver) { //clients play smoke locally depending on synced variable if(smokeon) particlesystem.play(); else particlesystem.stop(); } else { smokeon = particlesystem.isplaying; }
you can apply same reasoning sounds - sync variable represents type of sound played, , clients play sounds locally depending on synced variable.
Comments
Post a Comment