Search Unity

Question When small components become an army...

Discussion in 'Scripting' started by Rechronicle, Mar 28, 2022.

  1. Rechronicle

    Rechronicle

    Joined:
    Dec 14, 2017
    Posts:
    32
    So, how do you manage (quite a lot) small components?
    Currently struggling with Player ability which keeps added with new components to handle small things. (PlayerJump, PlayerMove, PlayerInteract, and so on)

    I'm thinking about making a RegisterMe method inside PlayerController, but getting stuck figuring out how to pass itself(the caller component) inside a method and assign it to its specific class variable slot.

    Any tips/insight will be helpful.
    Thanks!

    (edited the ambiguous word choice)
     
    Last edited: Mar 28, 2022
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    You'll have to elaborate. What do you mean by "spawning new components"? If it's for player behaviour(s), do they have to be actual components compared to plain old classes?
     
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,735
    RegisterMe is good, I've used that. The other way is to have a master script that adds other scripts in its start method. This works nicely because the master script can give the other scripts exactly what they need to do business.

    I like this pattern for adding components at runtime:

    Factory Pattern in lieu of AddComponent (for timing and dependency correctness):

    https://gist.github.com/kurtdekker/5dbd1d30890c3905adddf4e7ba2b8580

    Good naming of your parts helps a lot too, but even then it can get impressively long and complex.

    This is the "start up the player spaceship controller" code for my Jetpack Kurt Space Flight Game, which has now been in active development for nine (9) years and has seen over 6000 commits to the master branch. It's not pretty but it works and I still regularly update it.

    Code (csharp):
    1.     IEnumerator Start()
    2.     {
    3.         MyInput.Instance.SetInputGameMode( MyInput.InputGameMode.PLAYING_SPACE);
    4.  
    5.         // just to ensure they are updated
    6.         DSM.SpaceFlightVelocityY.Clear();
    7.         DSM.SpaceFlightVelocityX.Clear();
    8.         DSM.SpaceFlightRadarAltimeterString.Clear();
    9.  
    10.         // turn off helicopter mode if you don't have previews on, just in case
    11.         if (!DSM.Settings.EnablePreviewLevels.bValue)
    12.         {
    13.             DSM.SpaceFlightEnableHelicopterMode.bValue = false;
    14.         }
    15.  
    16.         WeatherController.InitGame();
    17.  
    18.         DSM.SpaceFlightPowerLevel.fValue = 0.0f;
    19.  
    20.         DSM.SpaceFlightEnginePSLevel.fValue = 0.0f;
    21.  
    22.         Game1.StartMissionTimers();
    23.  
    24.         FlightDataRecorder.Instance.StartGame();
    25.  
    26.         ScoreAdvanceTable.ResetBonusesForNextLegOfTravel();
    27.  
    28.         cam = Camera.main;
    29.  
    30.         FocusWatchPauseOnDeviceOnly.Create();
    31.  
    32.         var esc = gameObject.AddComponent<Escape>();
    33.         esc.NeedConfirmation = true;
    34.         esc.NeedPauseOverlay = true;
    35.         esc.OnEscape = () => {
    36.             DSM.SpaceFlightZeroLandingGameResult.Value = "Abandoned";
    37.  
    38.             DSM.SpaceFlightNotifyMissionInterrupted.Poke();
    39.  
    40.             Game1.GameOver( MissionStatusType.Abandoned);
    41.         };
    42.         if (configure.Instance.CTRLR)
    43.         {
    44.             esc.InTVMode = true;
    45.         }
    46.  
    47.         results.EnableNextButton = false;
    48.  
    49.         yield return null;
    50.  
    51.         CreateVABS();
    52.         OrientationChangeSensor.Create( transform,
    53.             () => {
    54.                 CreateVABS();
    55.  
    56.                 InitiateLabelHidingTimer();
    57.             }
    58.         );
    59.  
    60.         // angles camera down when weapons are equipped, versus using camera oblique frustum
    61.         SpaceFlightCameraObliquity.UseXAngleVersusObliquity = DSM.Combat.EquipPlayerWithWeapons.bValue;
    62.  
    63.         SetCameraObliquity();
    64.         OrientationChangeSensor.Create( transform, SetCameraObliquity);
    65.  
    66.         SpaceFlightGravitySetting.Current.ApplyToPhysicsSystem();
    67.  
    68.         GameObject player = new GameObject( "SpaceFlightPlayer");
    69.  
    70.         STATE.ThePlayer = player;
    71.            
    72.         GameObject visible = null;
    73.         {
    74.             int shipno = 0;        // DSM.SpaceFlightSelectedPlayerShip.iValue;
    75.  
    76.             if (DSM.SpaceFlightOverridePlayerShip.bValue)
    77.             {
    78.                 shipno = DSM.SpaceFlightNewGame.SelectedPlayerShip.iValue;
    79.             }
    80.  
    81.             shipno = PossiblePlayerShips.ConstrainIndex( shipno);
    82.  
    83.             GameObject prefab = PossiblePlayerShips.GameObjects[ shipno];
    84.             visible = Instantiate<GameObject>(prefab, player.transform);
    85.  
    86.             if (DSM.Settings.EnableVisibleControlGeometry.bValue)
    87.             {
    88.                 if (shipno < PlayerShipControls.Length)
    89.                 {
    90.                     var ctrls = Instantiate<GameObject>( PlayerShipControls.GameObjects[shipno], player.transform);
    91.  
    92.                     visibleControls = ctrls.GetComponent<SpaceFlightControlsGeometry>();
    93.                 }
    94.             }
    95.         }
    96.  
    97.         JetblastNozzle = new GameObject( "JetblastNozzle").transform;
    98.         JetblastNozzle.SetParent( player.transform);
    99.         JetblastNozzle.localRotation = Quaternion.Euler( 90, 0, 0);
    100.         JetblastNozzle.localPosition = Vector3.zero;        // driven elsewhere depending on camera
    101.         jetblast_feeler.Attach( JetblastNozzle,
    102.             () =>
    103.             {
    104.                 float amount = 30.0f * indicatedPower;
    105.                 return amount;
    106.             },
    107.             () =>
    108.             {
    109.                 return 8.0f;
    110.             }
    111.         );
    112.  
    113.         InitialCameraSetup( player, visible);
    114.  
    115.         Transform spawn = null;
    116.         while( spawn == null)
    117.         {
    118.             yield return null;
    119.             spawn = ProcessPlayerSpawnsAndObjectives();
    120.         }
    121.  
    122.         MoteCentroid = new GameObject ("MoteCentroid").transform;
    123.         MoteCentroid.SetParent (player.transform);
    124.         MoteCentroid.localPosition = Vector3.forward * 10.0f;    // always first person!
    125.         MoteSystem.Create(
    126.             MoteCentroid,
    127.             () => {
    128.                 if (rb)
    129.                 {
    130.                     return rb.velocity;
    131.                 }
    132.                 return Vector3.zero;
    133.             },
    134.             MasterSizeScale: 0.3f,
    135.             MasterCountScale: 2.0f
    136.         );
    137.  
    138.         Debug.Log( "Choosing spawn named " + spawn.name);
    139.  
    140.         Ray ray = new Ray( spawn.position + Vector3.up * 20, Vector3.down);
    141.         RaycastHit rch;
    142.         Vector3 pos = spawn.position;
    143.         if (Physics.Raycast( ray, out rch, 100))
    144.         {
    145.             pos = rch.point + Vector3.up * 2.0f;
    146.         }
    147.  
    148.         player.transform.SetPositionAndRotation( pos, spawn.rotation);
    149.  
    150.         rb = player.AddComponent<Rigidbody>();
    151.         // it's F***ing space vacuum baby!!
    152.         rb.angularDrag = 0.0f;
    153.         rb.drag = 0.0f;
    154.  
    155.         rb.centerOfMass = Vector3.zero;
    156.  
    157.         STATE.ThePlayerRB = rb;
    158.  
    159.         GenericImpactSensor.Attach(
    160.             rb,
    161.             OnImpactImpulse,
    162.             () => {
    163.                 if (!ready) return false;
    164.  
    165.                 if (YouHaveDied) return false;
    166.  
    167.                 return true;
    168.             },
    169.             OnceOnly: false
    170.         );
    171.  
    172.         DamagePropertyImpactSensor.Attach(
    173.             rb,
    174.             (deadlinessType) => {
    175.                
    176.                 if (!ready) return;
    177.  
    178.                 if (YouHaveDied) return;
    179.  
    180.                 TrackAppliedDamage(100);
    181.  
    182.                 MissionLogEntryCollection.Add( MissionLogType.SHIP_DAMAGED, "Deadly contact");
    183.  
    184.                 YouAreDead();
    185.  
    186.                 switch( deadlinessType)
    187.                 {
    188.                 default :
    189.                     break;
    190.  
    191.                 case DeadlinessType.SHAKING :
    192.                     PlayerType1DemiseShaking.Attach( rb.gameObject, alsoGameOver: false);
    193.                     break;
    194.  
    195.                 case DeadlinessType.ELECTROCUTION :
    196.                     PlayerType1DemiseElectrocution.Attach( rb.gameObject, alsoGameOver: false);
    197.                     PlayerType1DemiseShaking.Attach( rb.gameObject, alsoGameOver: false);
    198.                     break;
    199.                 }
    200.             },
    201.             () => {
    202.                 return true;
    203.             }
    204.         );
    205.  
    206.         BumpNoiseImpactSensor.Attach(
    207.             rb,
    208.             () => {
    209.                 if (!ready) return false;
    210.                 // doesn't matter if you died; we still keep making sound!
    211.                 return true;
    212.             }
    213.         );
    214.  
    215.         FuelConsumer = SpaceFlightFuelConsumer.Attach( gameObject);
    216.  
    217.         if (SpaceFlightFuelConfig.DoesNeedPurelyFuelBases())
    218.         {
    219.             System.Func<Color> GetRadarColor = () =>
    220.             {
    221.                 bool blink = (((Time.time * 3) % 1.0f) < 0.5f);
    222.                 if (FuelConsumer.IsLowFuelCritical)
    223.                 {
    224.                     return blink ? Color.yellow : Color.red;
    225.                 }
    226.                 if (FuelConsumer.IsLowFuelWarning)
    227.                 {
    228.                     return blink ? Color.yellow : Color.black;
    229.                 }
    230.                 return Color.cyan;
    231.             };
    232.  
    233.             GenericSiteSpawner.PlaceSpaceFlightFuel(
    234.                 spawn, spawn.position, GetRadarColor);
    235.  
    236.             yield return null;
    237.         }
    238.  
    239.         DSM.SpaceFlightDigitalFuelSeconds.Clear();
    240.         DSM.SpaceFlightDigitalFuelFormatting.Clear();
    241.         DSM.SpaceFlightDigitalFuelColor.Clear();
    242.  
    243.         CreateAppropriateInstruments();
    244.  
    245.         DSM.SpaceFlightEngineStatus.iValue = (int)SpaceFlightEngineStatusType.NORMAL;
    246.         DSM.SpaceFlightEngineOnLatch.bValue = false;
    247.  
    248.         SpaceFlightRadarAltimeter.Create( rb);
    249.         SpaceFlightHUD2.Create( rb);
    250.         SpaceFlightScoreHUD.Create();
    251.         if (DSM.Settings.EnableVelocitySlope.bValue)
    252.         {
    253.             SpaceFlightVelocitySlopeIndicator.Create();
    254.         }
    255.  
    256.         fpsf = FPSFollow.Attach (cam.gameObject, null);
    257.         fpsf.enabled = true;
    258.  
    259.         DisableRenderersWhenPlayerNear.PlayerTransform = player.transform;
    260.  
    261.         yield return null;
    262.  
    263.         LoadingOverlay.SetActive(false);
    264.  
    265.         if (DSM.SpaceFlightEnableHelicopterMode.bValue)
    266.         {
    267.             var rotor_attach_point = new GameObject( "rotor_attach_point");
    268.             rotor_attach_point.transform.SetParent( player.transform);
    269.             rotor_attach_point.transform.localPosition = Vector3.up * 1.0f;
    270.             rotor_attach_point.transform.localRotation = Quaternion.identity;
    271.  
    272.             System.Func<Quaternion> GetOrientation = () => {
    273.                 return rotorDiscVisualTilt;
    274.             };
    275.             System.Func<float> GetRPM = () => {
    276.                 return indicatedRPM;
    277.             };
    278.  
    279.             RotorSystem1.Attach( rotor_attach_point.transform, GetOrientation, GetRPM);
    280.         }
    281.  
    282.         System.Action OnFinishedIntroCamera = () => {
    283.             ready = true;
    284.  
    285.             STATE.PlayerTransform = player.transform;
    286.  
    287.             CommonStatisticsAccumulator.Attach();
    288.  
    289.             SpaceFlightMissionDecider.StartProperMission();
    290.  
    291.             {
    292.                 MissionLogEntryCollection.Add( MissionLogType.ANNOTATION, SpaceFlightFuelConfig.FullCurrentlySelectedFuelDescription());
    293.             }
    294.  
    295.             {
    296.                 MissionLogEntryCollection.Add( MissionLogType.ANNOTATION, SpaceFlightGravitySetting.FullCurrentlySelectedGravitySettingDescription());
    297.             }
    298.  
    299.             SETTINGS.MissionLogCreateWeatherReport();
    300.  
    301.             SpaceFlightRadar1.SupplyPlayerTransform( CameraSwivel, cam);
    302.  
    303.             InitiateLabelHidingTimer();
    304.  
    305.             DSEnableShadowsOnLight.Create();
    306.  
    307.             ThumbPositionReminder.Load();
    308.  
    309.             SpaceFlightProximityRefuelingSensor.SetDelegatesForAll( GetPlayerPosition, GetIsLanded);
    310.  
    311.             if (DSM.SpaceFlightEnablePitchLadder.bValue)
    312.             {
    313.                 SceneHelper.LoadScene( "SpaceFlightPitchLadderHUD", additive: true);
    314.             }
    315.  
    316.             ConfigureAppropriateCameraMode();
    317.  
    318.             if (trigger1) trigger1.enabled = true;
    319.  
    320.             SaveSystem.Instance.MarkAttempted();
    321.  
    322.             spaceflight_tv_dpad_briefing.Load();        // all guarded for editor, TV, etc.
    323.         };
    324.  
    325.         DSEnableShadowsOnLight.Create();
    326.            
    327.         {
    328.             if (trigger1) trigger1.enabled = false;
    329.  
    330.             IntroductoryCamera.Create(
    331.                 () => {
    332.                     return cam.gameObject.transform;
    333.                 },
    334.                 (fr) => { indicatedRPM = fr; },
    335.                 OnFinishedIntroCamera
    336.             );
    337.         }
    338.  
    339.         PulseRedDamage.Create (() => {
    340.             return RedFraction;
    341.         });
    342.  
    343.         SpaceFlightDarkNightMode.ConditionallyAttach( player.transform);
    344.  
    345.         SpaceFlightObscurationMode.ConditionallyAttach();
    346.  
    347.         if (HighLevelGameModeUtility.IsPlanetary())
    348.         {
    349.             SpaceFlightEngineMode.SelectPlanetaryEngineMode();
    350.  
    351.             NotifyWhenDisabled.Attach( gameObject,
    352.                 (goDummy) => {
    353.                     SpaceFlightStabilityAugmentationUtility.SelectTutorialStability();
    354.                     SpaceFlightEngineMode.SelectTutorialEngineMode();
    355.                     SpaceFlightEngineVariabilityConfig.SelectTutorialEngineVarability();
    356.                 }
    357.             );
    358.  
    359.             DSM.SpaceFlightStabilityAugmentationLevel.iValue = (int)SpaceFlightStabilityAugmentationType.NONE;
    360.  
    361.             // not sure this is good
    362. //            DSM.SpaceFlightEnableInflightEngineStop.bValue = true;
    363.         }
    364.     }
     
    Joe-Censored and Rechronicle like this.
  4. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,188
    If you are using AddComponent to add a component, then that call returns a reference to the component instance you just added. You can do whatever you want with it. Depending on how your stuff is setup, if the smaller components all use the same interface or inherit from a base class, a single method that you pass things to can work.

    If that isn't the case, you can still just pass it over for assignment after using AddComponent.
     
    Rechronicle likes this.
  5. Rechronicle

    Rechronicle

    Joined:
    Dec 14, 2017
    Posts:
    32
    pardon, my bad at choosing words. It's just like (PlayerInteract, PlayerJump, PlayerMove, etc) & not actually spawning a component. I'm not really familiar with working with a non-component script for now. But the functionality is mostly related to behavior.


    Gotta be honest I'm not at that level yet to understand how to implement Factory patterns, will look into it later on.
     
    Last edited: Mar 28, 2022