Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Anyone else ever feel like their programming is just plain bad?

Discussion in 'General Discussion' started by ptgodz, Jul 31, 2017.

  1. DominoM

    DominoM

    Joined:
    Nov 24, 2016
    Posts:
    460
    Here maybe.. It is a good parallel to iteration in game development, though we have an advantage in having a repeatable process (code) that can be improved rather than remade from scratch each time. It's kinda like if the first group had a CAD/CAM potters wheel so didn't need the repetition to build physical skill in quite the same way. If they had, I'd bet their bad pile was smaller than the second group when they got to the first "masterpiece".
     
  2. larku

    larku

    Joined:
    Mar 14, 2013
    Posts:
    1,412
    Yes, I was making few assumptions and covering all bases in 1 shot. That's not how I'd do it, but more information is better than not enough.

    Naa, I was not willing to make that assumption at all. I was avoiding "Oh yeah there were exceptions being thrown but that's something else" after 10 posts of trying to figure out the issue :)

    What I asked for (the print statements) make the least assumptions and covers the most ground for things I may not have thought of (the useless statements are of no cost, I didn't want to have to post "oh yeah, now whack a statement on line x for me"..

    We all have our own ways and reasons - I don't really get why you'd take issue with redundant debug statements, they're free and can highlight things you don't expect.

    Edit: Note, the trace printing you suggested there gives almost nothing that wasn't already being traced by his existing trace printing.
     
    Last edited: Aug 2, 2017
  3. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,244
    If exceptions were happening at that point, none of the rest of the update() would execute anyway. Plus you can look at the comments and you can see it works fine.

    I take issue exactly becasue Debug calls in Unity aren't free. They aren't even cheap.
    Try runnig a clock check every time you debug large section of code in that way. Then try removing Debug calls and check the improvement.
    10 Debug calls bring (for me, on average) 5ms delay. That is HUGE compared to just adding items to a list (which is done in at the rate of 1000 items per 1ms in worst case). Not to mention all the code you have to clean up afterwards. Debug calls may be cheap if you're unemployed, but they are in no way free. :)

    Edit: And prints are even worse because they make their own call to debug (extra jump).
     
    Last edited: Aug 2, 2017
  4. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    8,393
    Did half of this thread disappear suddenly? Could've sworn there were more pages.
     
  5. larku

    larku

    Joined:
    Mar 14, 2013
    Posts:
    1,412
    Again, I was not thinking of just specific possible issues. I was getting some output of value and *then* I was going to analyse what's going on. You like to work differently, fine.

    Common, they're not there for production. Even though I think that argument is nonsense in this situation (unless timing in involved with this error). What's more they're not Debug calls, they're print statements which do not carry the same cost as generating the stack trace for a Debug call.

    Cleaning up? "git checkout -- /path/to./file" fixes it every time.
     
  6. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,244
    Read the edit. They take even more time.
     
  7. larku

    larku

    Joined:
    Mar 14, 2013
    Posts:
    1,412
    Still, not an issue IMO in this instance. The update loop is single threaded so timing (in this instance) is not a likely cause, so totally moot, do you know how slow it is to step through code with the debugger (as you opened with)? If you really believe it's an issue then..
     
  8. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,244
    Well IMO it would be better to introduce the guy to the debugger instead of telling him to s*** up his code.
     
  9. larku

    larku

    Joined:
    Mar 14, 2013
    Posts:
    1,412
    Sure, go ahead.

    Right, yes that's what I was doing.

    Did you notice that the exercise *did* assist in the OP finding the solution.. Sure he should know how to use a debugger, but there's value in other techniques.
     
  10. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,734
    Sure debuggers exist. Frequently they are more of a pain to work with then just throwing in a couple of debug statements. I seldom go to the effort of firing up the full debugger.
     
    Ryiah and larku like this.
  11. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,244
    Of course. A couple. But did you see what he did? Print every other line.
     
  12. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,734
    It worked. Can't complain about that.
     
    QFSW, Ryiah and larku like this.
  13. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    3,822
    I think this is part of my point. "self explanatory" makes a number of assumptions about the developers level of competence and familiarity.

    A legit domain expert can quickly read and digest code of much, much higher complexity than a domain novice or intermediate. This is a large part of what makes someone an expert. This is part of the problem, what may be obvious for one developer may not be for another.

    Some code so banal as to be equally obvious to nearly all levels of proficiency, but this code doesn't usually do very much. Code that just pipes data verbatim from A to B is usually pretty obvious (and let's be honest, this is far and away what most code does). Code that deals in more and more transformation tends to require higher levels of competence (or at least focus and attention).

    I think you misunderstand me. I am not saying that "commenting code is a sign of weakness" - I'm saying that often times comments are an indicator that the developer had difficulty writing the code.

    The fact that sometimes a task is hard for us (or exceeds our skill level) should not be a point of shame. Programming is a skill, and there are times when the task meets or exceeds our skill levels.

    That we encounter tasks that exceed our skill level does not make us a 'bad programmer' - it makes us human beings who still have more to learn.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.SocialPlatforms;
    3. using UnityEngine.UI;
    4. using System.Collections.Generic;
    5. using System.Collections;
    6. using UnityEngine.Analytics;
    7. using UnityEngine.Advertisements;
    8. //using GooglePlayGames;
    9. using System;
    10.  
    11. public class PlayerController : MonoBehaviour {
    12.  
    13.     public Rigidbody PlayerRigid;
    14.     public GameObject ButtonControls;
    15.     public GameObject DeathPanel;
    16.     public GameObject SettingsButton;
    17.     //how much you want to jump, ex 250
    18.     public int JumpForce;
    19.     //average velocity AUTO-CALCULATED
    20.     public float AvgVelocity;
    21.     //how much more to jump when bouncing
    22.     public float BouncerFactor;
    23.     //distance from player to ground
    24.     public float DistanceFromGround;
    25.     //how much friction when rolling on groud
    26.     public float GroundFrictionAmount;
    27.     //how much you want to push on the X MAXIMUM, ex 200
    28.     public float XForceMax;
    29.     //how much you want to really move
    30.     //public float XForceReal;
    31.     //this is how much you want to bounce, ex 45
    32.     public int BounceForceFactor;
    33.     //this is automatically calculated, do NOT set it. At start it can NOT be zero
    34.     public float BounceForce = 1;
    35.     //this is when there is too much BounceForce
    36.     public float BounceForceOverMax;
    37.     //this is the minimum force needed to start a bounce, preventing infinite bouncing
    38.     public float BounceForceMin;
    39.     //this is to lower amount of force of bounce if ball is too powerful
    40.     public float BounceForceLimiter;
    41.     //this is when the ball is too high, set it lower than what you think is too high
    42.     public float TooHighY;
    43.     public float OvejumpProtectionAmount;
    44.     //this is when to limit the ball's jump speed, ex 10
    45.     public float MaxYVelocityAmount;
    46.     public bool MoveRight;
    47.     //distance text
    48.     public Text DistanceText;
    49.     public Image DistanceFillImage;
    50.     public float Distance;
    51.     public float DistanceRecord; //get from GOOGLE, NOT PLAYERPREFS
    52.     public float DistanceFillAmount;
    53.     public Vector3 OrigPosition;
    54.     //these are forces we use with BUTTONS
    55.     public int ButtonForceSides = 150;
    56.     public int ButtonForceUp = 300;
    57.     //this is position of checkpoint to revive at
    58.     public Vector3 Checkpoint;
    59.     //this is for ads
    60.     public GameObject AdFail;
    61.     public GameObject GooglePromo;
    62.     public Text GooglePromoText;
    63.     public string DeathObjectName;
    64.     //don't forget to change GooglePlay.cs's ID as well
    65.     public string LeaderboardID;
    66.     public float hue, S, V;
    67.     public Color GreenColor;
    68.     public string DeathEffects = "Death Effects";
    69.     public Text DeathText;
    70.     public float EffectLength;
    71.     public GameObject Explosion;
    72.     public AudioSource SliceSound;
    73.     public AudioSource ExplosionSound;
    74.     private GameObject GravityObject;
    75.     public Text AnnounceText;
    76.     public string SavePlayerPrefName;
    77.     private int TimesSampled;
    78.     private float TotalVelocity;
    79.     // Use this for initialization
    80.     void Start ()
    81.     {
    82.         //save original position
    83.         DistanceRecord = PlayerPrefs.GetFloat(SavePlayerPrefName);
    84.         //PlayGamesPlatform.Activate();
    85.         OrigPosition = transform.position;
    86.         if (PlayerPrefs.GetInt("AskAboutGoogle1stTime") == 0)
    87.         {
    88.             //ask to log in
    89.             GooglePromo.SetActive(true);
    90.             Debug.Log("Asking to log in");
    91.         }
    92.  
    93.         if (PlayerPrefs.GetInt("PlayerLoggedInGoogle") == 1)
    94.         {
    95.             //player has shown interest in logging in, so do it!
    96.             GetPlayerDataWithUnitySocial();
    97.             Debug.Log("Player has logged in before, so loggin in now");
    98.         }
    99.  
    100.         if (PlayerPrefs.GetInt("PlayerLoggedInGoogle") == 2)
    101.         {
    102.             //don't log in, this player isn't too interested
    103.             GooglePromo.SetActive(false);
    104.             Debug.Log("Player declined log in");
    105.         }
    106.     }
    107.  
    108.     void GetPlayerDataWithUnitySocial()
    109.     {
    110.         Debug.Log("Trying to get player data...");
    111.         Social.localUser.Authenticate(success =>
    112.         {
    113.             if (success)
    114.             {
    115.                 GooglePromoText.text = "Thanks! You're logged in :)";
    116.                 Debug.Log("Authentication successful");
    117.                 string userInfo = "Username: " + Social.localUser.userName +
    118.                     "\nUser ID: " + Social.localUser.id +
    119.                     "\nIsUnderage: " + Social.localUser.underage;
    120.                 Debug.Log(userInfo);
    121.                 DistanceRecord = PlayerPrefs.GetFloat(SavePlayerPrefName);
    122.                 Debug.Log("DistanceRecord set from Google!");
    123.                 GooglePromo.SetActive(false);
    124.             }
    125.  
    126.             else
    127.             {
    128.                 //if can't log in, use local high score
    129.                 DistanceRecord = PlayerPrefs.GetFloat(SavePlayerPrefName);
    130.                 GooglePromoText.text = "Oh no, login failed! If you don't have internet, please connect and try again.";
    131.                 Debug.Log("Unity-social login FAIL");
    132.             }
    133.  
    134.             Social.LoadScores(LeaderboardID, scores => {
    135.                 if (scores.Length > 0)
    136.                 {
    137.                     Debug.Log("Got " + scores.Length + " scores");
    138.                     string myScores = "Leaderboard:\n";
    139.                     foreach (IScore score in scores)
    140.                     {
    141.                         myScores += "\t" + score.userID + " " + score.formattedValue + " " + score.date + "\n";
    142.                         if (Social.localUser.id == score.userID)
    143.                         {
    144.                             Debug.Log("This user's score is: " + score.formattedValue);
    145.                             //since we're connected, let's pull Google's high score instead of local one
    146.                             DistanceRecord = Convert.ToSingle(score.formattedValue);
    147.                             //also, let's update our local high score to match Google's
    148.                             PlayerPrefs.SetFloat(SavePlayerPrefName, DistanceRecord);
    149.                             Debug.Log("Updated local distance record from Google!");
    150.                             return;
    151.                         }
    152.                     }
    153.                     Debug.Log(myScores);
    154.                 }
    155.  
    156.                 else
    157.                 {
    158.                     Debug.Log("No scores loaded");
    159.                     //if nothing on leaderboard, use local score and hope more people download this s***
    160.                     DistanceRecord = PlayerPrefs.GetFloat(SavePlayerPrefName);
    161.                 }
    162.             });
    163.         });
    164.         //post local score in case the player had a better offline score
    165.         //google should take care of the rest
    166.         DistanceRecord = Mathf.RoundToInt(PlayerPrefs.GetFloat(SavePlayerPrefName));
    167.         Social.ReportScore(Mathf.RoundToInt(DistanceRecord), LeaderboardID, (bool success) =>
    168.         {
    169.         });
    170.     }
    171.  
    172.     public void ShowLeaderboard()
    173.     {
    174.         Social.ShowLeaderboardUI();
    175.     }
    176.  
    177.     // Update is called once per frame
    178.     void Update ()
    179.     {
    180.         //Debug.Log("Player X velocity: " + PlayerRigid.velocity.x);
    181.         //if ball jumps too high
    182.         if (DistanceFromGround > TooHighY)
    183.             {
    184.             //apply a small push towards the ground
    185.                 InitiateOverjumpProtection();
    186.             }
    187.         //raycast distance to ground
    188.         RaycastHit hit;
    189.         Ray downRay = new Ray(this.transform.position, Vector3.down);
    190.         if (Physics.Raycast(downRay, out hit))
    191.         {
    192.             //get distance to groud
    193.             DistanceFromGround = hit.distance;
    194.         }
    195.  
    196.         if (PlayerRigid.velocity.y >= MaxYVelocityAmount)
    197.         {
    198.             //remove its velocity
    199.             PlayerRigid.velocity = new Vector3(PlayerRigid.velocity.x, 0, PlayerRigid.velocity.z);
    200.             Debug.Log("Y velocity limited");
    201.         }
    202.         CalcDistanceText();
    203.         CalcAvgVelocity();
    204.  
    205.         if (Input.GetKeyDown("a"))
    206.         {
    207.             LeftButton();
    208.         }
    209.  
    210.         if (Input.GetKeyDown("d"))
    211.         {
    212.             RightButton();
    213.         }
    214.  
    215.         if (Input.GetKeyDown("space"))
    216.         {
    217.             JumpUp();
    218.         }
    219.     }
    220.  
    221.     void OnCollisionEnter(Collision collision)
    222.     {
    223.         //reset the bounce force limiter
    224.         BounceForceLimiter = 1;
    225.         //calculate how much bounce force to add based on the limiter and bounce magnitude
    226.         BounceForce = BounceForceFactor * collision.relativeVelocity.magnitude * BounceForceLimiter;
    227.         if (BounceForce > BounceForceOverMax)
    228.         {
    229.             //if too much then limit bounce
    230.             LimitBounce();
    231.             //now that we've limited the bounce force, use it
    232.             PlayerRigid.AddForce(Vector3.up * BounceForce);
    233.             return;
    234.         }
    235.  
    236.         if (BounceForce > BounceForceMin)
    237.         {
    238.             //if greater than minimum bounce force value. Do this so it
    239.             //doesn't bounce forever
    240.             PlayerRigid.AddForce(Vector3.up * BounceForce);
    241.         }
    242.  
    243.         if (collision.gameObject.CompareTag("Death"))
    244.         {
    245.             //if it hits something bad (flames, spikes etc...)
    246.             KillPlayer();
    247.         }
    248.     }
    249.  
    250.     void OnTriggerEnter(Collider collision)
    251.     {
    252.         //again kill player, just if it hits a trigger
    253.         //DEATH just deactivates. Add other tags for death effects.
    254.         if (collision.gameObject.CompareTag("Fire"))
    255.         {
    256.             //show fire effect, wait, then die
    257.             DeathText.text = "Roasted!";
    258.             StartCoroutine(ShowDeathEffect());
    259.             Explosion.SetActive(true);
    260.             ExplosionSound.Play();
    261.         }
    262.  
    263.         if (collision.gameObject.CompareTag("NoGravity"))
    264.         {
    265.             PlayerRigid.AddForce(Vector3.up * 30);
    266.             Physics.gravity = new Vector3(0, -3F, 0);
    267.             StartCoroutine(TimeWithGravity());
    268.             GravityObject = collision.gameObject;
    269.             collision.gameObject.SetActive(false);
    270.             AnnounceText.text = "Low gravity!";
    271.             StartCoroutine(Announce(5));
    272.         }
    273.  
    274.         if (collision.gameObject.CompareTag("Impale"))
    275.         {
    276.             //show fire effect, wait, then die
    277.             EffectLength = 1;
    278.             StartCoroutine(ShowDeathEffect());
    279.             DeathText.text = "Impaled!";
    280.             PlayerRigid.constraints = RigidbodyConstraints.FreezeAll;
    281.         }
    282.  
    283.         if (collision.gameObject.CompareTag("Death"))
    284.         {
    285.             KillPlayer();
    286.             DeathText.text = "Unknown death!";
    287.         }
    288.  
    289.         if (collision.gameObject.CompareTag("Bullet"))
    290.         {
    291.             KillPlayer();
    292.             DeathText.text = "Shot!";
    293.         }
    294.  
    295.         if (collision.gameObject.CompareTag("Smash"))
    296.         {
    297.             DeathText.text = "Smashed!";
    298.             KillPlayer();
    299.         }
    300.  
    301.         if (collision.gameObject.CompareTag("Zap"))
    302.         {
    303.             DeathText.text = "Electrocuted!";
    304.             KillPlayer();
    305.         }
    306.  
    307.         if (collision.gameObject.CompareTag("Slice"))
    308.         {
    309.             DeathText.text = "Sliced!";
    310.             SliceSound.Play();
    311.             KillPlayer();
    312.         }
    313.  
    314.         //if player hits bounce point, bounce the player
    315.         if (collision.gameObject.CompareTag("Bouncer"))
    316.         {
    317.             StartCoroutine(BounceAllower());
    318.             PlayerRigid.AddForce(Vector3.up * JumpForce * BouncerFactor);
    319.         }
    320.  
    321.         if (collision.gameObject.CompareTag("BouncerExtra"))
    322.         {
    323.             StartCoroutine(FasterTime());
    324.             Time.timeScale = 1.5f;
    325.             AnnounceText.text = "Faster time!";
    326.             StartCoroutine(Announce(15));
    327.         }
    328.  
    329.         if (collision.gameObject.CompareTag("TriggerCube"))
    330.         {
    331.             Checkpoint = collision.transform.position;
    332.             DeathObjectName = collision.gameObject.GetComponent<UnitStopper>().UnitParent.name;
    333.         }
    334.     }
    335.  
    336.     void OnCollisionStay(Collision collision)
    337.     {
    338.         //add optional ground friction
    339.         PlayerRigid.drag = GroundFrictionAmount;
    340.     }
    341.  
    342.     public void JumpUp()
    343.     {
    344.         //make the ball jump up ONLY if it isn't too high
    345.         if (DistanceFromGround < TooHighY)
    346.         {
    347.             PlayerRigid.AddForce(Vector3.up * JumpForce);
    348.         }
    349.     }
    350.  
    351.     public void JumpX()
    352.     {
    353.         //move left-right
    354.         //PlayerRigid.velocity = Vector3.zero;
    355.         if (MoveRight)
    356.         {
    357.             PlayerRigid.AddForce(Vector3.right * ButtonForceSides);
    358.         }
    359.  
    360.         else
    361.         {
    362.             PlayerRigid.AddForce(Vector3.left * ButtonForceSides);
    363.         }
    364.     }
    365.  
    366.     void LimitBounce()
    367.     {
    368.         //limit bounce if needed
    369.         BounceForceLimiter = (BounceForceOverMax / BounceForce);
    370.         BounceForce = BounceForce * BounceForceLimiter;
    371.     }
    372.  
    373.     void InitiateOverjumpProtection()
    374.     {
    375.         //this protects the ball from jumping too high, ex out of the screen's viewing range
    376.         //however it does add force down, which is why we have LimitBounce too
    377.         //PlayerRigid.AddForce(Vector3.up * OvejumpProtectionAmount * -1);
    378.         //Debug.Log("OverjumpProtection activated");
    379.     }
    380.  
    381.     void KillPlayer()
    382.     {
    383.         this.gameObject.SetActive(false);
    384.         DeathPanel.SetActive(true);
    385.         ButtonControls.SetActive(false);
    386.         SettingsButton.SetActive(false);
    387.         //now let's get some data
    388.         Analytics.CustomEvent("gameOver", new Dictionary<string, object>
    389.         {
    390.             { "DeathUnit", DeathObjectName},
    391.         });
    392.         Debug.Log("Analyzed, died at: " + DeathObjectName);
    393.         if (Distance > PlayerPrefs.GetFloat(SavePlayerPrefName))
    394.         {
    395.             //save new distance
    396.             PlayerPrefs.SetFloat(SavePlayerPrefName, Distance);
    397.             //report to Google
    398.             int HighScore = Mathf.RoundToInt(PlayerPrefs.GetFloat(SavePlayerPrefName));
    399.             Social.ReportScore(HighScore, LeaderboardID, (bool success) =>
    400.             {
    401.             });
    402.                 Debug.Log("New distance record saved! " + PlayerPrefs.GetFloat(SavePlayerPrefName));
    403.         }
    404.     }
    405.  
    406.     public void RevivePlayer()
    407.     {
    408.         ShowRewardedAd();
    409.     }
    410.  
    411.     public void ShowRewardedAd()
    412.     {
    413.         if (Advertisement.IsReady("rewardedVideo"))
    414.         {
    415.             var options = new ShowOptions { resultCallback = HandleShowResult };
    416.             Advertisement.Show("rewardedVideo", options);
    417.         }
    418.     }
    419.  
    420.     void CalcDistanceText()
    421.     {
    422.         //just in-game to calculate how far the ball went
    423.         Distance = (this.transform.position.x - OrigPosition.x) * -1;
    424.         Distance = Mathf.RoundToInt(Distance);
    425.         //get DistanceRecord on start, getting it from PlayerPrefs is too slow for Update
    426.         if (Distance > DistanceRecord)
    427.         {
    428.             DistanceText.text = "New record: " + Distance;
    429.             DistanceText.color = GreenColor;
    430.         }
    431.         else
    432.         {
    433.             DistanceText.text = "Distance: " + Distance;
    434.         }
    435.         //now for the bar
    436.         DistanceFillAmount = Distance / DistanceRecord;
    437.         DistanceFillImage.fillAmount = DistanceFillAmount;
    438.     }
    439.  
    440.  
    441.     IEnumerator BounceAllower()
    442.     {
    443.         float origYMax = MaxYVelocityAmount;
    444.         MaxYVelocityAmount = 99;
    445.         yield return new WaitForSeconds(1);
    446.         MaxYVelocityAmount = origYMax;
    447.     }
    448.  
    449.     //on-screen sontrol buttons
    450.     public void LeftButton()
    451.     {
    452.         MoveRight = false;
    453.         JumpX();
    454.     }
    455.  
    456.     public void RightButton()
    457.     {
    458.         MoveRight = true;
    459.         JumpX();
    460.     }
    461.  
    462.     public void JumpButton()
    463.     {
    464.         JumpUp();
    465.     }
    466.  
    467.     //this is to show and award the ad
    468.     private void HandleShowResult(ShowResult result)
    469.     {
    470.         switch (result)
    471.         {
    472.             case ShowResult.Finished:
    473.                 Debug.Log("The ad was successfully shown.");
    474.                 FinishReviving();
    475.                 break;
    476.             case ShowResult.Skipped:
    477.                 Debug.Log("The ad was skipped before reaching the end.");
    478.                 break;
    479.             case ShowResult.Failed:
    480.                 Debug.LogError("The ad failed to be shown.");
    481.                 break;
    482.         }
    483.     }
    484.  
    485.     void FinishReviving()
    486.     {
    487.         PlayerRigid.velocity = Vector3.zero;
    488.         this.transform.position = Checkpoint;
    489.         this.gameObject.SetActive(true);
    490.         DeathPanel.SetActive(false);
    491.         ButtonControls.SetActive(true);
    492.         SettingsButton.SetActive(true);
    493.     }
    494.  
    495.     public void LogInToGoogle()
    496.     {
    497.         GooglePromoText.text = "Thanks! Now logging in...";
    498.         GetPlayerDataWithUnitySocial();
    499.         PlayerPrefs.SetInt("PlayerLoggedInGoogle", 1);
    500.         Debug.Log("Player wants to log into Google");
    501.     }
    502.  
    503.     public void DeclineGoogleLogIn()
    504.     {
    505.         PlayerPrefs.SetInt("AskAboutGoogle1stTime", 1);
    506.         PlayerPrefs.SetInt("PlayerLoggedInGoogle", 2);
    507.         GooglePromo.SetActive(false);
    508.         Debug.Log("Player doesn't want to log into Google. Ouch!");
    509.     }
    510.  
    511.     void CalcAvgVelocity()
    512.     {
    513.         TimesSampled++;
    514.         TotalVelocity += PlayerRigid.velocity.x;
    515.         if (TimesSampled == 8)
    516.         {
    517.             AvgVelocity = TotalVelocity / TimesSampled;
    518.             TotalVelocity = 0;
    519.             TimesSampled = 1;
    520.         }
    521.     }
    522.  
    523.     IEnumerator ShowDeathEffect()
    524.     {
    525.         yield return new WaitForSeconds(EffectLength);
    526.         KillPlayer();
    527.     }
    528.  
    529.     IEnumerator TimeWithGravity()
    530.     {
    531.         yield return new WaitForSeconds(5);
    532.         Physics.gravity = new Vector3(0, -9.8F, 0);
    533.         GravityObject.SetActive(true);
    534.     }
    535.  
    536.     IEnumerator FasterTime()
    537.     {
    538.         yield return new WaitForSeconds(15);
    539.         Time.timeScale = 1;
    540.     }
    541.  
    542.     IEnumerator Announce(int AnnounceTime)
    543.     {
    544.         AnnounceText.gameObject.SetActive(true);
    545.         yield return new WaitForSeconds(AnnounceTime);
    546.         AnnounceText.gameObject.SetActive(false);
    547.     }
    548. }
    549.  
    Glancing through this code, it does seem that you struggled a bit. I would assume that you were fairly new to some of these kinds of problems and you were trying to figure stuff out as you went.

    The comments (to some degree) show that you were working through the problems as you wrote it.

    There's nothing wrong with that. You were learning and experimenting while also trying to achieve your goal. I don't think you would call yourself a grand master level game dev, so why should your code need to pretend that you are?
     
  14. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    26,726
    Fact is the grand master of code is dead. Everyone is working on top of other people's code these days so just get something that's bug free and you've basically won.
     
    larku and Kalladystine like this.
  15. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    6,673
    Most code that is standard stuff is copy-pasted and modified a little to suit the target. The rest is stuff the author has to actually work to figure out and is original work.

    If I'm not sure how to do something when I start then I normally write out a bunch of comments for what I want it to do, as 'objective headers', then start putting code under those headers until I'm done. The comments help me walk through it in my head and compartmentalize it. Later I pretty much always trash those comments because the code is readable enough to understand easily.

    When I'm done with the thing, class, or whatever, I immediately add helpful, concise and descriptive xml data to the class, methods and sometimes variables because I'm probably going to come back in 3 weeks and not know what the heck is going on.

    The main argument with comments is that the code should have been readable enough to not require the comment in the first place. This is true, and you should always strive to name variables descriptively and write code that is easy to understand. If you're writing math stuff, maybe its more readable if you just use letters - that is a subjective thing and no one can tell you what to do on that. In the end, you want to be able to approach whatever code you write with no idea how it works, read it over, and have a clear grasp on it due to the fact that it is clearly written - not commented.
     
    Kiwasi and hippocoder like this.
  16. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    26,726
    I don't understand the "comments are evil" thing. What is wrong with code that describes itself and comments describing why such a solution is chosen?

    That's pretty valuable, because there are a couple of things that spring to mind:

    1. the original design may have to be hacked or changed a bit as the project matures. This is because sometimes there's deadlines or a game needs to ship, and refactoring for a week can cause more damage and add many more bugs than a simple hack+comment to extend functionality.

    2. optimisations happened.
     
    angrypenguin, Kiwasi and DroidifyDevs like this.
  17. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    3,822
    The main problem with comments is that they tend to be a maintenance problem. An outdated comment can be crazy troublesome and misleading.

    So generally, it's preferred to keep comments minimal, so as to reduce the chance of them being outdated.

    Bad comments don't throw exceptions.

    I am not in the 'comments are evil' camp - but whenever possible - communicating with good names and stuff is obviously better.
     
    mysticfall likes this.
  18. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    26,726
    Um that's pretty insane. I've never done an outdated comment for decades. It is right next to the code. Why would it ever be outdated? Maybe I comment differently. I don't comment what I'm doing. I comment WHY I'm doing it.

    Maybe everyone else is commenting wrong.
     
    angrypenguin and AntoineDesbiens like this.
  19. HolBol

    HolBol

    Joined:
    Feb 9, 2010
    Posts:
    2,884
    I always feel like the code I wrote any time in the past was bad, and I could have done bettwe. That's just the nature of learning. If you start learning an instrument- like electric guitar, you'll learn to play the riff from Smoke on the water, and then with enough practice, you'll be playing way better than those simple power chords, keeping up with the best of the best, and you'll look back and think "did I really suck that bad?".

    You'll learn how to get better the more code you write, and the more constructs and ideas you can sensibly apply. Then it'll just become second nature. Keep going, keep improving, and keep learning.
     
  20. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    3,822
    Probably about 50% of my inline comments are notes about hacks.

    I think the code that I personally had the most difficulty writing was camera code, and looking over that code, it has the most comments of anything in my project.

    Code (csharp):
    1.  
    2.       // SIMPLE COLLINEAR - just offset with forward
    3.       if (Mathf.Abs(cross.y) < .3f ){
    4.         cross = Vector3.Cross( dir_ab, (dir_ac - forward).normalized );
    5.       }
    6.  
    7.       // REALLY COLLINEAR: forward is also colinear, so we need to add arbitrary offset.
    8.        float max_to_cross_y = .05f;
    9.       if (Mathf.Abs(cross.y) < max_to_cross_y) {
    10.         Vector3 upordown = cross.y >= 0 ? Vector3.up : Vector3.down;
    11.         float how_much = Mathf.Abs( cross.y ).ClampedAtLeast( max_to_cross_y );
    12.         Vector3 f_offset = Vector3.Lerp(forward, Vector3.Cross(dir_ab, upordown), how_much);
    13.         Vector3 f = f_offset;
    14.         float f_length = 1;
    15.         cross = Vector3.Cross(dir_ab, (dir_ac - f * f_length).normalized);
    16.       }
    17.  
    In this code for example, I'm trying to calculate perpendicularity, and there are problem states when multiple positions are collinear (or approaching).

    This code is obviously a cludge. I wasn't aware of this problem ahead of time, and tweeked the code (twice) after realizing there were multiple problems. At some point I should revisit this and apply a more thorough solution. It's likely that I won't unless there are game play problems.

    This kind of comment isn't going to go out of date, because it's documenting why - but more importantly because the entire operation is inline and requires around 5+ lines to complete.

    In other words, changing any single line is unlikely to render the comment meaningless. If I were to delete the entire paragraph - it's pretty likely the comment would go as well.

    Code (csharp):
    1.  
    2. public void ExampleMethod(){
    3.   // need to reset some variable for some reason
    4.   ExampleVariable = 0;
    5.   ...
    6.   ...
    7. }
    8.  
    This example is something that can go out of date. ExampleVariable may not need to be zero'd out, the code here is dependent on state that's being rewritten outside the method, so changes elsewhere in the file can make the comment invalid.

    Further, this code is a single line - so almost any change would render the comment obsolete.

    Often times, this kind of code is changed to be:
    Code (csharp):
    1.  
    2. public void ExampleMethod(){
    3.   // need to reset some variable for some reason
    4.   //ExampleVariable = 0;
    5.   ...
    6.   ...
    7. }
    8.  
    At this point, the comment is obsolete.

    Or far worse...
    Code (csharp):
    1.  
    2. public void ExampleMethod(){
    3.   // need to reset some variable for some reason
    4.   [line deleted]
    5.   ...
    6.   ...
    7. }
    8.  
    This comment is now totally out of place. The statement the comment refers to has been deleted, but the comment remains.

    Of course, that code is not ideal in the first place - but things like that happen in real life - especially when people are working long hours, tired, stressed or distracted.
     
    Last edited: Aug 2, 2017
  21. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    It's the same reason why test cases tend to incur additional maintenance cost, as you have to keep them up-to-date whenever you change the subjects they test. And as with tests, it doesn't mean that writing tests or adding comments is necessarily a bad thing per se. It's just that they are not free so we need to be careful so that they wouldn't cost more than they are really worth.

    It's generally recommended to refactor your code rigorously, and when we do that we often have to add or remove existing properties or methods, or change their names. And if everyone of them comes with matching comments or test cases, we need to make necessary changes to them also, which means they incur additional maintenance cost.

    And while excessive comments are not necessarily bad in themselves, they might be a symptom of some other problems, like API being designed not intuitively enough, or method implementation dealing with multiple concerns which makes it needlessly long or complex, and such.
     
    Kiwasi and hippocoder like this.
  22. Billy4184

    Billy4184

    Joined:
    Jul 7, 2014
    Posts:
    5,401
    I used to have the idea that code should be fully documented, i.e., you should be able to remove all code and still everything make a coherent story, but I'm pretty much the other way around now. For one thing, I found myself writing comments full of uppercase and exclamation marks that felt like I was 'barking' at myself, maybe on the assumption that I was coding half-asleep. Also the comments tended to vary a lot in concision depending on whether I was literarily inspired at the time, and sometimes I found that the code for one reason or another could tell a much deeper and more complete story than the comments themselves, and in much less space.

    Now I tend to only write descriptions for methods, as well as sections of code that happen to be difficult to look at and understand straight away (such as trigonometric calculations etc). If I'm writing asset store stuff I use more comments than I otherwise would.

    I think that, as long as method headers and such are included, and each line of code is marginally self-explanatory, code should be generally written on the assumption that you are attuned to what you are doing, because this assumption actually induces you to be more alert and receptive to what you're looking at. At least that works for me.
     
  23. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    26,726
    This is true where there are the resources to support it. One has to consider if the proper approach you outline, is the right approach for a project.

    In game development, the deadlines are often quite harsh, and code review, rigorous refactoring and other perfect-world ideals have to be abandoned or you start haemorrhaging money because you added 6 months to a 5 month project.

    And that 5 month project, if it used the perfect ideals to begin with, would be marked down as a 10 month project. I'm saying cutting corners is a necessary evil in unfortunately, too many cases.

    That's not to say I don't do that, I do - I refactor as often as I can afford to, but there are limits to how much time I can spend on an area of the game's code.

    One thing to bear in mind is that every single refactor of a large code base like in a typical game that will be finished, has the potential to introduce a ton of bugs, and that's pretty expensive and time consuming to test, and in a lot of cases, a studio just won't allow it.

    Then building around the no-go zones requires a fair bit of documentation upkeep and so on. Basically I'm trying to say there's still a gap between ideal code we all want to do and what we all end up having to do.
     
  24. derf

    derf

    Joined:
    Aug 14, 2011
    Posts:
    349
    LOL :D

    This is so true. I still remember my days of catalog every action of the user into the database; writing out the code in long hand, putting braces around every single IF statement and ELSE IF and ELSE; the list goes on and on of the ways I would write code which now is bad too even look at.

    Now I program like a crotchety old man.
     
  25. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    3,822
    Absolutely correct.

    I've been a professional dev for 20 years. I've never seen anything like game dev in terms of absolutely demanding corners being cut.

    This also of course depends on goals and resources... but man. it's tough to juggle everything. Especially if you're trying to achieve a lot on a small budget.
     
    Ryiah, larku and hippocoder like this.
  26. Deleted User

    Deleted User

    Guest

    I heavily comment because if I take a break from doing coding to work on art for e.g. I'll look back a couple of weeks later and forget what the hell I was doing and / or trying to achieve. Of course decently named variables / methods etc. help..

    If you've every worked on a massive codebase like a game engine comments are a necessity, no matter how smart you think you are.. Well unless they are utter **** that confuses you more than having no comments at all..

    Agree with @hippocoder fully, you can always tell who's a new(ish) coder in games dev because they cling a little too dearly to principles and methodology.. Whereas in actual development it's not about doing it the "correct" way, it's about the most efficient way (and generally functional lazyness if it's quicker).. Whilst everyone's running around with their pants on fire.

    In a lot of productions the words "ahh screw it" pop up quite regular. There are of course some exceptions, like creating tools for mass consumption which has an emphasis on readable and "correct" code.. When developing internal projects whether big or small creation it's just like artwork I suppose, there isn't enough time for it to be perfect so you get through it and mask problems best you can..
     
    Last edited by a moderator: Aug 2, 2017
    mysticfall, Ryiah and Kiwasi like this.
  27. AntoineDesbiens

    AntoineDesbiens

    Joined:
    Jun 9, 2014
    Posts:
    843
    thesecret.jpg
     
    Ryiah and frosted like this.
  28. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    3,822
    I'm way more on the side of naming than comments. I've spent hours arguing with people about how to name a method.

    A few months ago, I had a 35+ email thread with some programmer friends talking about how to name a simple extension method.

    Good names really go a long way in cutting down how many comments you might need.

    Everyone says "oh just name stuff good" - the truth is - naming things well is really tough. The most successful programmer I know is incredible with coming up with short, accurate, descriptive names. I've never seen anyone name methods/variables better.

    It's really a skill, and it helps to have a very strong vocabulary.
     
    Ryiah, Kiwasi and mysticfall like this.
  29. derf

    derf

    Joined:
    Aug 14, 2011
    Posts:
    349
    Lets see...

    I moderately comment my interfaces. The implementations of those interfaces get the most comments.

    I heavily comment abstract classes and base classes.

    I moderately comment my methods.

    Method names tend to explain what they are going to do and to what, so if there is a comment it is simple.

    My variables tend to be named in relation to what they do or represent. If I need to comment a // will do followed by a simple comment or definition.

    I make use of ternary statements, bit operators and using statements where appropriate.

    I make use of Try->Catch->Finally.

    The one thing I do is when I do a code change I include comments on the code change, the date, by who and a comment on why the change was required/needed/requested.


    So what do you all do?
     
  30. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    I think I can safely say that it's a discouraged practice, because most of the people nowadays use commit logs for that purpose for a good reason.

    I remember such a practice was more common back in the days when relatively few developers use such tools like CVS, or VSS and such. But as now when virtually everyone use more powerful SCM tools like Git, there's little reason to keep the history of changes inside comments, since it will clutter the codebase pretty quickly.

    I agree with you, but I'd like to make a provision that only API level comments (Javadoc, or those start with '///' in C#) are considered absolute necessary by a general consensus.

    I've never worked on any professional, or even collaborative game development project before, but I've done quite a few non-game projects in such a context, which have quite large codebase (normally with thousands of classes).

    And in all of such projects, I tried to minimize implementation level comments, and I believe I'm not alone in such a practice, because many respectable large open source projects also seem to do the same.

    I suspect that such a peculiar culture in game development might have more to do with the fact that it's relatively uncommon to actively develop and maintain game projects for a long time (like more than 10 years), than the matter of time or budget constraints which, of course exists also in other types of projects like business applications where people are more concerned about design or architecture.

    I don't know... As I said above, I've never done any game projects before so it's entirely possible that there might be other reason why more experienced game developers do way that do now.

    But it's been my impression so far that it's just because game projects tend to be more 'volatile' and much less dependent on the quality of source code to success. So I suspect it might be the main reason why many common software engineering practices seem to be not as widely accepted as it is the case with other fields.
     
    frosted likes this.
  31. Deleted User

    Deleted User

    Guest

    For base line stuff / training and general documentation with a modern engine you'd just write documents, to try and explain everything that's going on in such a complex piece of software it would take up too much real estate and probably just annoy your co-workers. You'll not find that much in the way of line level comments, mainly as you specified outlines for things like "interesting problems" where another dev might walk in the next day and think what the hell is this supposed to be doing? It looks wrong.

    The more descriptive you are the better, because all they'll do is ring you on your day off and say hey Shadow what's this actually doing?

    Generally it's probably been written a specific way to address a specific problem, so that's where you'll write a description or if it's a mass consumption engine the developers will generally add friendly hints like the following:

    Code (CSharp):
    1. APlayerController* MyPC = GetMyPlayerControllerFromSomewhere();
    2. UWorld* World = MyPC->GetWorld();
    3.  
    4. // Like object iterators, you can provide a specific class to get only objects that are
    5. // or derive from that class
    6. for (TActorIterator<AEnemy> It(World); It; ++It)
    7. {
    8.     // ...
    9. }
    When I say "heavily" commenting, I don't mean you add a vector to move a character up and I write a line // comment saying this is a vector to move a character up. I mean I don't bother with external documentation, so if I come across something that I might not find glaringly obvious the next time I come around to it I'll add a line descriptor.. Personally see no reason not to, it only takes a couple of minutes and it might save me a couple of hours headache.

    Sometimes I can be working on artwork for a month, so it's prudent to gracefully ease myself back into the coding workflow.

    One of the negatives of working in small teams / solo, being a jack of all trades your all over the place and you have the limitation of being human..

    We're not talking MS paint here, things like engines are extremely complicated and ridiculously mammoth sized, there are many teams that deal with a lot of niche components whether it's lighting / rendering to audio / cross platform support. They encumber many languages and in short they are plain huge.

    Let's just put it this way, I was speaking to one of the engineers at Epic as to why they're "backlogging" bugs and they just said straight up we just do not have the time to address them all and some are tied into subsystems that would need a complete re-factor from the ground up in which we'd need multiple different actual issues to consider it.

    It's not peculiar, it's a lack of time.. The only way you'll find out is if you get something like UE and crack open the source code, then you'll realise why.. As far as code bases in engines go it's pretty clean but even the game logic API's for their "game framework" can be a little all over the place and bit of a "black box".. I'm sure if you'd care to listen @neginfinity would give you a boat load of examples.

    Even from a small "indie's" viewpoint, if you're working on a somewhat large project the same logic applies.. I'd love to continuously refactor code but as @hippocoder says it's going to add time on and frankly in most occasions with modern hardware it's completely unjustified. If it's a problem fix it, if it's not move on..
     
    frosted and neginfinity like this.
  32. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    Even though I'm absolutely new to game development, I'm not really so to development in general. Actually, it's more like platform or framework projects that I mainly design at work, rather than 'MS Paint like' applications as you said (though I don't see why such type of programs cannot be designed with the same level of quality as others).

    And, so called 'technical debts' are a commonly observed problem in many large projects, but the concept of continuous refactoring is also quite common among them. Actually, I see the latter as a specifically devised to prevent the former situation as long as possible.

    Of course, it's inevitable that the codebase becomes more rigid as it grows larger and takes more and more effort to change some fundamental part of the project. But the major part of software engineering is devoted to fight that specific problem, and the common remedies like continuous refactoring or unit testing, and etc is actually embraced by people who work on large projects outside the game development industry.

    So, if it was due to the nature of most video game projects that they are more art heavy, and tend to have much shorter life span, then I might see why game developers tend to be more reluctant to embrace such well established practices.

    But if you say that it's just because they lack time or money, I have to say that I'm not convinced because it's not just game projects that face such a problem, and developers who work in other fields actually do employ such practices like continuous refactoring or even TDD in large scale projects.

    And lastly, refactoring has not much to do with optimization, although sometimes it's done to improve performance. In most cases, they refactor their codebase to make it more flexible to changes, and it was why I claimed that the concept of continuous refactoring might be devised specifically to deal with the problem with technical debts.
     
    Last edited: Aug 3, 2017
    frosted and hippocoder like this.
  33. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,244
    I can still complain how it was done.
    Yandere simulator also works. But this still triggers me:
    https://i.redd.it/gyubzyq87xmx.png
     
  34. larku

    larku

    Joined:
    Mar 14, 2013
    Posts:
    1,412
    Except for suggesting that the debugger would have been better*, the rationale you gave for why it irks you so much was baseless. From this end you seem more interested in attacking the approach than understanding why I chose it.

    I was essentially being lazy and just blindly adding trace (which somewhat simulates stepping through the method). This was guaranteed to give me the information I'd require to see the execution path.

    I already knew that the list was not being modified as the OP suggested so finding the real execution path was all I was interested in. My approach does that well - objective achieved. How that irks you I just don't understand.

    IMO the time required to find appropriate places to add trace would have taken longer than just shoving them in and removing them and I may have missed an ideal place to add them. I make mistakes.

    Likewise if I was using the debugger I'd likely have set the break point at entry to the method and not in the area I suspected the problem was and I'd step from there, just like I was with the trace I suggested.

    I get that it's not the approach you'd take, but IMO it's not the hideous crime you're suggesting. It's a valid approach that works.

    *I explained why I didn't suggest a debugger and agreed that a debugger is a great way to investigate the issue, but they're a bit cruddy to use from a forum thread.
     
  35. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    8,393
    But you don't really have a reason to complain if you are not the code maintainer, it is not your project and the project works for the user.
     
  36. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,406
    This is probably the ugliest piece of code I have written in our game. Its barely readable thanks to the fluent syntax you get with Linq,also it contains probably one of my only comments in our entire game :p

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using System.Linq;
    3. using System.Text;
    4. using Assets.Scripts.Network;
    5. using Assets.Scripts.Weapons;
    6. using Scripts.DamgeSystem;
    7. using Scripts.Inventory;
    8. using Scripts.MaterialSystem;
    9. using UnityEngine;
    10.  
    11. namespace Scripts.Weapons.Shotguns
    12. {
    13.     public class NetworkedTubeFedShotgun : NetworkedFirearm
    14.     {
    15.         private ShotgunHit[] hitData = Enumerable
    16.             .Range(0, 5)
    17.             .Select(i => new ShotgunHit { Id = i })
    18.             .ToArray();
    19.  
    20.         protected override void RayCastBullet(Vector3 pos, Vector3 direction)
    21.         {
    22.             var allHits = hitData
    23.                .Select(d =>
    24.                 {
    25.                     RaycastHit hitInfo;
    26.                     Physics.Raycast(pos,
    27.                         Quaternion.Euler(Random.Range(-1.5f, 1.5f), Random.Range(-1.5f, 1.5f), Random.Range(-1.5f, 1.5f))*direction, out hitInfo, LocalFirearm.EffectiveRange, bulletHitMask);
    28.                     d.HitInfo = hitInfo;
    29.                     return d;
    30.                 })
    31.                 .Where(d => d.HitInfo.transform)
    32.                 .Select(d =>
    33.                 {
    34.                     if (d.HitInfo.transform.gameObject.layer == playerLayer)
    35.                     {
    36.                         var hitZone = AvatarController.GetHitZone(d.HitInfo.transform);
    37.                         d.Damage = CalculateDamage(d.HitInfo);
    38.                         d.AvatarId = hitZone.Avatar.NetworkedId;
    39.                         d.HitZoneId = hitZone.hitZoneId;
    40.                     }
    41.                     else
    42.                     {
    43.                         d.AvatarId = null;
    44.                         d.HitZoneId = d.Id; //Ugly hack to get ignore groupby for none player hits
    45.                         d.Damage = 0;
    46.                         d.Died = false;
    47.  
    48.                         d.Material = MaterialManager.Instance.GetMaterial(d.HitInfo).Type;
    49.                         Impact(d.Material, d.HitInfo.point, d.HitInfo.normal);
    50.                     }
    51.  
    52.                     return d;
    53.                 })
    54.                 .GroupBy(h => new { h.AvatarId, h.HitZoneId })
    55.                 .Select(group =>
    56.                 {
    57.                     var hit = group.First();
    58.                     if (group.Key.AvatarId.HasValue)
    59.                         hit.Damage = group.Sum(h => h.Damage);
    60.  
    61.                     return hit;
    62.                 })
    63.                 .Select(d =>
    64.                 {
    65.                     if (d.AvatarId.HasValue)
    66.                         d.Died = ApplyDamage(d.AvatarId.Value, d.HitZoneId, d.Damage, d.HitInfo.point);
    67.  
    68.                     return d;
    69.                 })
    70.                 .OrderBy(h => h.AvatarId.HasValue ? 0 : 1)
    71.                 .ToList();
    72.  
    73.             if (allHits.Count == 0)
    74.             {
    75.                 RPC("Fire", NetworkReceivers.Others, pos, direction, true);
    76.                 return;
    77.             }
    78.  
    79.             var playerHits = allHits
    80.                 .Where(h => h.AvatarId.HasValue)
    81.                 .ToList();
    82.  
    83.             var impacts = allHits
    84.                 .Where(h => !h.AvatarId.HasValue)
    85.                 .ToList();
    86.  
    87.  
    88.             var points = allHits.Select(h => h.HitInfo.point).ToArray();
    89.             var avatarIds = playerHits.Select(h => h.AvatarId.Value).ToArray();
    90.             var hitZoneIds = playerHits.Select(h => h.HitZoneId).ToArray();
    91.             var damages = playerHits.Select(h => h.Damage).ToArray();
    92.             var diedStates = playerHits.Select(h => h.Died).ToArray();
    93.  
    94.             var materials = impacts.Select(h => (int) h.Material).ToArray();
    95.             var normals = impacts.Select(h =>  h.HitInfo.normal).ToArray();
    96.  
    97.             RPC("FireMultipleProjectiles", NetworkReceivers.Others, points, avatarIds, hitZoneIds, damages, diedStates, materials, normals);
    98.         }
    99.  
    100.         [BRPC]
    101.         public void FireMultipleProjectiles(Vector3[] points, ulong[] avatarIds, int[] hitZoneIds, float[] damages, bool[] died, int[] materials, Vector3[] normals)
    102.         {
    103.             Fire(Vector3.zero, Vector3.zero, true);
    104.  
    105.             for (int i = 0; i < points.Length; i++)
    106.             {
    107.                 var point = points[i];
    108.                 if (avatarIds.Length > i)
    109.                 {
    110.                     RegisterDamge(avatarIds[i], hitZoneIds[i], damages[i], died[i], point);
    111.                 }
    112.                 else
    113.                 {
    114.                     var index = i - avatarIds.Length;
    115.                     Impact((Materials)materials[index], point, normals[index]);
    116.                 }
    117.             }
    118.  
    119.         }
    120.  
    121.         private class ShotgunHit
    122.         {
    123.             public int Id { get; set; }
    124.             public RaycastHit HitInfo { get; set; }
    125.             public ulong? AvatarId { get; set; }
    126.             public bool Died { get; set; }
    127.             public int HitZoneId { get; set; }
    128.             public Materials Material { get; set; }
    129.             public float Damage { get; set; }
    130.         }
    131.  
    132.     }
    133. }
    134.  
     
  37. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    8,393
    This actually has decent level of readability, however it will generate ton of garbage every time you fire it. Linq methods will generate garbage, same applies to simply having an anonymous delegate.
     
    frosted likes this.
  38. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,406
    Its a user generated action. Our team have set a level where we produce no garbage in Update methods or methods that can be batched/looped etc.
     
  39. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,244
    I'm the one who runs it. If your argument is that
    "One can have not reason to complain about matters that they have no control over",
    let's consider the following:

    [willful action that hasn't resulted in negative consequences before]-
    You buy a box of juice that you've been buying every week for, let's say years.
    [negative consequence that one has no control over:]-
    You drink it and later discover the juice was compromised / made from a bad fruit batch and you're now stuck in bed with <whatever horrible thing you can get> for a about a week or two or maybe the taste is just horrible.
    Do you not have a reason to complain because you didn't prepare or box the juice?
    I would say you have a pretty good reason to complain as the boxed juice company can, as the result of your public complaint, implement more rigorous quality assurance measures. Or (alternatively) bear the brunt of public opinion, decrease in sales and so on and so forth.

    Why I drew this parralel:

    [willful action that hasn't resulted in negative consequences before]-
    When I decided to back the game (Yandere simulator) it was much simpler and the small amount of string comparrison taking place didn't really make a dent in performance.
    [negative consequence that one has no control over]-
    Then as the complexity went up (number of NPC present and ways to kill added and, following that, number of string comparisons), so did the CPU performace decrease.
    Do I not have a reason to complain?
    I disagree. If I file a substantiated complaint, explaining what is not being done efficienty and hurting CPU performace, the developer can improve the code and again, everybody wins.

    Am I missing something? Have I misconstrued what you were saying?

    I suggested debbuger BECAUSE OF THE WAY you placed the prints, not becasue you placed them. I'm not arguing that one method is, without exception, better than the other. I'm arguing that if you would have to log every other line to get rid of ONE bug, you might as well use a debugger, not that debugger should be used for everything.
    I also wanted to suggest you missed a great opportunity to "teach a man to fish". (the first reply I made to you in this thread.)
    And now that we are on the same page:
    No, no. Please. I insist.
     
    Last edited: Aug 3, 2017
  40. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,734
    If you are so concerned about it, go back to the original thread and make a comment. The only way you'll improve code shared in the scripting forum is to participate.
     
  41. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    3,822
    I really feel that I need to write more classes with names like NetworkedTubeFedShotgun. :D
     
    larku and AndersMalmgren like this.
  42. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    8,393
    Then you have no argument. If it has been thoroughly tested it'll behave the same fashion as properly done solution with consts and enum. Besides, end user don't even get to run it, and works with compiled bytecode instead.

    This kind of stuff concerns only original developer and code maintainers. Nobody else.

    If anything... there seems to be a pattern where people who "want to do things properly" are less likely to actually finish a project. A finished game full of horrible hacks is a finished product, and an unfinished game with clean code is not a finished product.
     
    Martin_H likes this.
  43. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,406
    Since that class ended up having nothing todo with the internal magazine logic it should be renamde to only NetworkedShotgun :D
     
    frosted likes this.
  44. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,244
    If we are talking about the image I posted, I did.
    And if we are talking about the placement of prints, I am, since the person who posted that is in this thread and I wanted to know why he did not opt to use a debugger without confusing the other thread's OP with a debugger which he could evidently do without. And this IS a programming thread.

    That's an assumption.
    And what do you think compiled bytecode is compiled from?
    Are you proposing that dev runs it without compiling? Thoroughly tests it, even?
    Did you even read the previous post?
    What does this have to do with the discussion?

    Read the previous post and answer the questions.
     
  45. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    3,822
    I can't speak for everyone, as I frankly have limited experience.

    In my personal experience game code:
    • Is more focused on writing and transforming data, instead of reading/piping like most traditional apps.
    • has more subsystems that require strict coordination and fluid interaction than most other domains.
    • deals with a much wider range of problem set, simply, there is a much wider range of functionality.
    • can require larger scale iteration (user level design iteration)
    The amount of complexity that needs to be managed in games can really be daunting.

    Most business functionality revolves around piping data (largely verbatim - or renamed/grouped) from one point to the other: read data from data source, pipe data to presentation, read user entered data, write user data modification back to source. Most modern applications have almost gotten rid of user data entry/modification. Now it's just "list items, select item".

    Enterprise software has a lot of challenges, I'm not saying it's all trivial. It's not.

    I will say that I think that most of the cost in terms of achieving goals in enterprise software has more to do with a lot of different people and a lot of different teams having a lot of different priorities, and less about the actual functionality itself. In many cases, the functionality itself can be an almost secondary or tertiary concern.

    OTOH, a guy on a massive AAA game team may have an experience far closer to traditional enterprise software. At this point, I would imagine that the utility of things like TDD can start to outweigh their costs.
     
    Deleted User likes this.
  46. Deleted User

    Deleted User

    Guest

    I try to avoid these types of discussions because they get long, drawn out and excessively semantical.. What exactly are we talking about here? On one side we have an "organic" mammoth multi-discipline piece of software.

    Then we have "game logic" which is what game developers mostly concern themselves with, it's apples and oranges to game engine theory.. Game engines do in a lot of ways follow their own "best practices" and beyond, modularity has become a rather large thing in modern game engine theory to try and avoid a lot of ad hoc re-factoring. Something a two thousand line space shmup will never concern itself with..

    That's where a lot of confusion seems to stem from, like pretty much everything in your game it is very much dependant on your project as to what you should consider.. I was clearly stating oddities within rigidity for coding principles whereas Weinberg / Hoare / Summerville's best practices when it comes down to brass taxes is just common sense really.

    Also let's not start comparing to enterprise practices because they're on a different wavelength, how you'd optimise your GLSL / HLSL code has nothing to do with their principles..

    I don't see them concerning themselves much in the way of avoiding use of sin, cos and pow for the amount of ALU's, in Unity you're better off concerning yourself with overhead of garbage collection / shaders / general render budgets etc. than stressing yourself over dumb stuff like how should I comment my code and how neat and tidy should I brace my method? It DOES NOT MATTER!. (I mention this because I have seen coders have a go at N00bs for such things)..

    If you find it more comfortable to comment every line you write, whilst it might not be the most effiecient way of doing things if it helps you again does not matter in the slightest. So yeah, it's partly to do with time, it's partly to do with money but ultimatley it's because in the grand scheme of things a lot of it means jack..

    I'm not trying to convince you of anything, as ultimatley you do things how you see fit and it doesn't affect me.. I've been coding with games for well over a decade and I do things how I prefer to do things, simple as.

    Pick your battles..
     
    Last edited by a moderator: Aug 3, 2017
    sylon likes this.
  47. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    3,822
    I just wanted to say that I really agree with this also.

    The development lifecycle for "in house" software is really different from the "box and ship" model. It's only natural that these two very different worlds are best served with different approaches.

    I see a lot of games moving over to process that's closer to "in house" software if games move more to "software as a service" with ongoing constant development. These kinds of approaches are probably in place more for most F2P games with constant content releases for example.

    Most of the "best practices" in software are costly precautions to prevent regression. Stuff like testing and even fundamental thoughts on how OO should be approached are almost all designed to prevent regression shifting burden (sometimes massively) to other aspects of development (test driven literally integrates regression testing into authoring specification). This kind of thing really makes sense in a 'never ending dev cycle that spans decades' - it does not make sense if the dev process has a strict end point.

    "Technical debt" doesn't matter so much if in three months you never have to make another payment.
     
    mysticfall likes this.
  48. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,406
    I count on that we can make atleast one more game with the same core mechanics as our current game, I'm sure more games than ours sees reused code.
     
  49. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    @ShadowK

    I think the key difference between my opinion and yours lies in the question that if, and how much game development is different from other developments in general.

    But first of all, I'm not trying to convince everyone that game development should be done in this or that way. I don't know if you have had an impression that I am, but I'm really not - as I already stated earlier, I'm absolutely new to game development, and I'm not that arrogant as to think I already know everything that is needed to do game development simply because I've been working as a professional developer in some other field for quite a long time.

    That being said, I don't think they are so much different from each other that we cannot really talk about any common values, principles, or practices at all.

    Please remind that this discussion has grown out of a question about commenting practice, than moved into the matter of refactoring code continuously. Does game development so much different from every other types of development that its developers even have different commenting habits or don't value refactoring as others do?

    I agree with @frosted in that the key differences between game development and business application development lie in where each of them puts their emphasis, namely making system flexible to changing requirements and maintainable in the long run, or handling some complex interactions between multiple subsystems in realtime with the most efficiency, respectively.

    So, if I was arguing that every game project should embrace things like TDD, you might have a point in saying that such 'enterprise practices' have little relevance in game development, even though I suspect it can be at least open for a discussion at least (Unity already supports unit testing quite well).

    But if you say that we shouldn't even refactor continuously when developing games, simply because they tend to be short on time and budget, then I have to say I'm not convinced - there are a lot of business system projects that run on insane time and budget constraints, especially in startups, but they often do care about things like refactoring their code.

    If you say that you've completed many successful game projects without doing continuous refactoring or by commenting every single lines of your code, I believe you did. As you said, "whatever works", and I'm also not trying to convince you to change your habits.

    All I'm saying is, I believe that I'm entitled to my own opinion as much as any other more experienced game developers, regarding such fundamental aspects of development like commenting code or value of refactoring code.

    I'm not claiming that my opinions on these matters should be an absolute truth because I've been writing softwares in another field for the past 20 years or so.

    But on the other hand, I don't think writing codes in a game development is so much different from what I've been doing so far, that even in such fundamental matters like commenting practice, coding conventions (they do matter in any type of collaborative projects, in my opinion) or refactoring code, I must uncritically accept whatever more experienced game developers say about the subject.
     
    Last edited: Aug 3, 2017
    frosted and AndersMalmgren like this.
  50. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    26,726
    Thing is though - you are right. I have been doing game development for a few decades. The way you code from what I can glean, is ideal. I do it myself - when I can.

    It's logical that a constant refactor and adhering to some standards produces better code. And as you are (I assume) working for yourself, you should continue in this manner until you can't.

    You'll know when you can't when it becomes a waste of time to do so. Here's a few things in game development that can throw spanners in our ideals (where applicable):
    • studio deadline (they're much shorter in game dev)
    • refactor might break too much stuff
    • unity engine is bugged, need a workaround
    • realtime games design is a moving target, a constantly moving target
    • optimisation (much more important in game dev) along with moving target makes refactoring somewhat risky, depending on the case
    My daily coding habits are to constantly try to keep the codebase relevant, relatable, reliable! But sometimes not worth the effort/time :)
     
    angrypenguin and mysticfall like this.
unityunity