Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

[AI Challenge] Capture the Flag

Discussion in 'General Discussion' started by Samuel411, Mar 20, 2017.

  1. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    Hm, the idea isn't so bad. Maybe I come back to it.
     
  2. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    I found a problem with the bullets. Often, I have the situation that my bot should hit the enemy but the bullet goes through him. You can see this in the screenshot. The bullet is moving too fast for Continuous detection mode of the rigidbody component.



    You can solve this problem when you set the detection mode of the bullets to ContinuousDynamic and to Continuous for the soldiers.
     
    Samuel411 likes this.
  3. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    You can also clearly see the AimAt bug here the red trooper is looking at the blue but his shot will hit the wall. Note the AimAt fix I posted should still work in your AI code (it was not added to the challenge API).
    Judging by your Blue Troopers laser light you are also suffering from the AimAt bug.
     
    Last edited: Mar 28, 2017
  4. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    Yes, maybe. But your AimAt method isn't allowed for the challenge till Samuel adds it to the API.
     
  5. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    It just offsets the point you lookat (to allow for muzzle offset), so anyone can add it to their soldier and use the existing LookAt() call. You can after all instruct your soldiers to look anywhere you want. ;)
     
  6. Hoogin

    Hoogin

    Joined:
    Jul 27, 2015
    Posts:
    38
    The problem is obvious using rigid-body physics isn't ideal for bullets due to their high velocity.
    Most games uses a raycast or multiple timed raycasts forming a trajectory to simulate ballistics.

    Since they are already looking like space marines for the sake of simplicity make them have laser rifles instead and use a simple raycast for collision detection.
     
    Master-Frog and Kiwasi like this.
  7. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    Yes, but you're rotating the transform directly in the method and you're using methods of the Physics class. I'm thinking this is against the rules.
     
  8. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    I was going to post a little "ambush" ai for people to test against. But the project is a newer version of unity and I can't upgrade right now.

    Sharing info and approaches is also the best way to protect from cheesy strats. If people have examples they can mess around with, they can find counter strats - or raise a flag if something is truly OP.
     
  9. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    I think that's just to get the LookAt rotation/vector, you can generate that using Quaterian.LookAt(). It just needs a bit of trigonometry to convert this rotation based solution to an offset position based one.
     
  10. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    I'm thinking this should be no problem because Samuel allows just code and no prefabs. I understand this rule so that my main class derives from SoldierWrapper and all other classes are either no MonoBehaviour classes or are attached on the fly in the Awake method.
    Therefore you don't need to attach the whole project but just your code for the AI
     
    Samuel411 likes this.
  11. Oribow

    Oribow

    Joined:
    Nov 9, 2012
    Posts:
    38
    Will probably join this contest, if I can mange it.
    It would be good, if samuel411 could add a little reminder to use namespaces to the rules. Else it might create avoidable name conflicts.
    Also, would you allow id generation based on class ref obtained from a raycast?
     
  12. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    0 so far I'm not expecting many this early perhaps mid April. Ill have a counter set up as soon as I begin receiving them and post them as a dll if they allow it.
     
  13. Billy4184

    Billy4184

    Joined:
    Jul 7, 2014
    Posts:
    6,009
    I'll hopefully start this weekend, have a few things on my plate til then.
     
    Samuel411 likes this.
  14. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    I'll probably just rename classes and files. I'm not sure what you mean by the second part?
     
  15. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    I'm thinking he means that everyone should use an unique namespace in his scripts for the classes. Otherwise you could get a name conflict e.g. when two people name their class both Sniper
     
  16. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    As long as people have their Initials or Name in the class name it should work out OK.

    I think the idea is to be able to monitor the enemy, with a Unique tag not just a changing location, it would allow for scouting/tracking and maybe even 'damage estimation' e.g. concentrated fire on weakest enemy tactics?
     
  17. Oribow

    Oribow

    Joined:
    Nov 9, 2012
    Posts:
    38
    @Samuel411 If you want to rename the classes and files, that's ok I guess. But namespaces were invented exactly to prevent these kind of name conflicts in a clean way.
    Exactly what I meant. Wouldn't break the current rules in anyway.
     
  18. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    I'll rename classes to match their forums name followed by an incremental integer before sharing a dll to avoid any issues.
     
    Last edited: Mar 28, 2017
  19. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    That should be fine :) Interesting idea.
     
  20. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    I implmented your collision detection settings and it seems to be working well. I also implemented a change to the look at method which I think fixed the issue with the look at.
     
  21. Oribow

    Oribow

    Joined:
    Nov 9, 2012
    Posts:
    38
    Just out of interest, is there a reason why namespacing is avoided in this case? Just curious... ;)
     
  22. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    I'd rather not add to the rules something so simple like namespacing lol. Its not a big deal either really because they'll probably be renamed anyway when I make the final video.
     
  23. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    A namespace wouldn't be a "rule" thing -- it just keeps your API class names (and other types, enums for example) from conflicting with type names somebody else decides to use in their AI. They just put a using declaration in their files referencing the namespace you wrap your classes with, and go about their business interacting with your API.
     
  24. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    I'm not going to require it. If you feel it necessary to add a namespace go for it but I'll be renaming classes and files anyway to differentiate between peoples entries during judging. Your submitting your required classes for your submission. If you make a class type with a generic name wrap it with a namespace declaration but in unity they are not really necessary tbh unless your doing more advanced things.
     
  25. Not_Sure

    Not_Sure

    Joined:
    Dec 13, 2011
    Posts:
    3,546
    I'm really enjoying this.

    I think I may try my hand at it.

    In the least it will give me an excuse to FINALLY work with GitHub.
     
    Samuel411 likes this.
  26. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    You won't regret it :)
     
  27. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194


    Great intro to game AI, which I thought you might find interesting.
     
    Samuel411 likes this.
  28. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    It's not a full fix as you are still using a raycast, so the wide bullets can still be blocked by edges. If you make it a sphere cast with a radius slightly larger than the bullet's radius this will be spot on.
    OK this does not fix it you are still using the basic Look at from/to the soldiers centres only with the test from the guns muzzle. Please consider my AimAt solution.
     
    Last edited: Mar 29, 2017
  29. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    True, but it's a little hard to figure out whats going on if you can't actually open the scenes or look at the prefabs or run it.

    Still, since nobody else has mentioned it, I guess it's not a major problem. Probably only a couple people stuck running older versions.
     
  30. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    OK My fist Entry and hopefully this will be helpful for other developers...

    Meet SoldierArowxRnd and tremble at it's super power of random plays!

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class SoldierArowxRnd : SoldierWrapper
    6. {
    7.  
    8.     private NavAgentExample navAgent;
    9.  
    10.     public bool returningToSpawn = false;
    11.  
    12.     public bool damaged = false;
    13.     public Vector3 damagedLocation;
    14.  
    15.     public bool enemySpotted = false;
    16.  
    17.     public float ignoreDamageTimer = 2;
    18.     private float _curIgnoreDamageTimer;
    19.  
    20.     // The idea is to make a AI that will run random plays against an AI your working on
    21.     private static int counter = 0;
    22.     private static List<SoldierArowxRnd> team = new List<SoldierArowxRnd>(8);
    23.  
    24.     private static List<Vector3> plays = new List<Vector3>(8);
    25.  
    26.     public int number = 0;
    27.  
    28.     void Awake()
    29.     {
    30.         number = counter++;
    31.  
    32.         team.Add(this);
    33.  
    34.         navAgent = GetComponent<NavAgentExample>();
    35.  
    36.         if (plays.Count == 0)
    37.         {
    38.             int c = Random.Range(1, 8);
    39.  
    40.             float gx = (GridManager.instance.gridSize.x * 0.5f);
    41.             float gy = (GridManager.instance.gridSize.y * 0.5f);
    42.  
    43.             Vector2 pos;
    44.  
    45.             for (int i = 0; i < c; i++)
    46.             {
    47.                 pos.x = (float)Random.Range(-gx, gx);
    48.                 pos.y = (float)Random.Range(-gy, gy);
    49.                 plays.Add(GridManager.instance.FindClosestCell( pos, true ).GetPosition());
    50.  
    51.                 Debug.Log("Play " + i + " " + plays[i].ToString());
    52.             }
    53.         }
    54.  
    55.         base.Awake();
    56.  
    57.         SetName("ArowxRnd");
    58.  
    59.      
    60.     }
    61.  
    62.     void Start()
    63.     {
    64.         base.Start();
    65.  
    66.         int play = number % plays.Count;
    67.         Debug.Log("Soldier " + number + " using play " + play);
    68.         navAgent.targetCell = GridManager.instance.FindClosestCell(plays[play]);
    69.  
    70.         //navAgent.targetCell = GridManager.instance.FindClosestCell(enemySpawnLocation);
    71.  
    72.     }
    73.  
    74.     void CheckTimer()
    75.     {
    76.         // Stop looking at the location we were damaged from after the timer
    77.         if (Time.time >= _curIgnoreDamageTimer)
    78.         {
    79.             damaged = false;
    80.         }  
    81.     }
    82.  
    83.  
    84.     public Vector3 directionTestVector3;
    85.     void Update()
    86.     {
    87.         base.Update();
    88.  
    89.         CheckTimer();
    90.  
    91.         // If we were damaged then look at where we damaged, spray!
    92.         if (damaged)
    93.         {
    94.             if(LookAt(damagedLocation))
    95.                 Shoot();
    96.             return;
    97.         }
    98.  
    99.         // Check if a path exists
    100.         if (navAgent.pathGenerated.Count > 0)
    101.         {
    102.             // if there's an enemy then dont keep moving, shoot them!
    103.             if (!enemySpotted)
    104.             {
    105.                 // Move in the direction of the next path node
    106.                 MoveTowards(navAgent.pathGenerated[0]);
    107.                 LookAt(navAgent.pathGenerated[0]);
    108.             }
    109.         }
    110.         else
    111.         {
    112.             if ((!enemySpotted) && (!soldier.HasFlag()) && !returningToSpawn)
    113.             {
    114.                 navAgent.targetCell = GridManager.instance.FindClosestCell(enemySpawnLocation);
    115.             }
    116.         }
    117.  
    118.         if (soldiersInSight.Count > 0)
    119.         {
    120.             // See if any soldiers in sight are on the enemy team
    121.             IAgent targetSoldier = null;
    122.             for (int i = 0; i < soldiersInSight.Count; i++)
    123.             {
    124.                 if (soldiersInSight[i].GetTeam() != soldier.GetTeam())
    125.                 {
    126.                     targetSoldier = soldiersInSight[i];
    127.                 }
    128.             }
    129.  
    130.             // enemy spotted?
    131.             enemySpotted = targetSoldier != null;
    132.  
    133.             // Any enemy soldiers in sight?
    134.             if (targetSoldier != null)
    135.             {
    136.                 // Look at them, if we are, shoot.
    137.                 if (LookAt(targetSoldier.GetLocation()))
    138.                 {
    139.                     // pew pew
    140.                     Shoot();
    141.                 }
    142.             }
    143.         }
    144.         else
    145.         {
    146.             // No soldiers so no enemey could be spotted
    147.             enemySpotted = false;
    148.         }
    149.  
    150.         if (soldier.HasFlag() && !returningToSpawn)
    151.         {
    152.             returningToSpawn = true;
    153.  
    154.             navAgent.targetCell = GridManager.instance.FindClosestCell(spawnLocation);
    155.         }
    156.     }
    157.  
    158.     public override void DamageCallback(Vector3 location)
    159.     {
    160.         // We took damage
    161.  
    162.         // location is the location of the enemy when they shot
    163.  
    164.         // Stop looking at the location we were damaged from after the timer
    165.         _curIgnoreDamageTimer = Time.time + ignoreDamageTimer;
    166.         damagedLocation = location;
    167.         damaged = true;
    168.     }
    169.  
    170.     public override void FlagStatusChangedCallback(IGrabable flag)
    171.     {
    172.         // A flag was grabbed or ungrabbed.
    173.  
    174.         // the other team's flag was grabbed or dropped? Lets go back to our spawn and clear it up
    175.         if (flag.GetTeam() != soldier.GetTeam())
    176.         {
    177.             navAgent.targetCell = GridManager.instance.FindClosestCell(spawnLocation);
    178.         }
    179.     }
    180.  
    181.     public override void SoldierDiedCallback(IAgent agent)
    182.     {
    183.         // A soldier has died
    184.     }
    185. }
    186.  

    It also shows how easy it can be to make a simple AI that could be programmed to do a specific Play... so should be handy for setting up quick play tests e.g. What happens when they do this against my AI...



    ...Could be tested with a little bit of hard coding added to soldierArowxRnd.cs
     
    chelnok and frosted like this.
  31. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    I'd prefer a method that will automatically rotate the agent to fix the issue. From what I saw in your method you report if the bullet will be able to hit the enemy using a sphere cast. My method however will offset the agents rotation to the left since the weapon is on the right. The offset will also give a little more rotation to make up for the small radius of bullets. I've tested the current fix before releasing it and have had no issues after 10 trials. Maybe lucky? Ill test it again since your reporting issues with it.
     
    Not_Sure likes this.
  32. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    Ideally your variables should be hardcoded when you're done and a instructions.txt if you have a more complicated entry.

    If you have something in your scene make it a prefab and send it in as a zip file.

    When I say hardcoded I mean when you declare them like
    Code (CSharp):
    1. public float foo = 0.5f
     
  33. Not_Sure

    Not_Sure

    Joined:
    Dec 13, 2011
    Posts:
    3,546
    Well shoot, there goes one exploit I noticed out of the bag. :D
     
  34. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Code (CSharp):
    1. public bool LookAt(Vector3 position)
    2.     {
    3.         bool lookingAt = false;  
    4.  
    5.         if (_soldier.IsDead())
    6.             return false;
    7.  
    8.         // calculate gun offset needed to aim gun at target postion
    9.  
    10.         Vector3 lastRotation = transform.eulerAngles;
    11.  
    12.         Transform gun = _soldier.currentWeapon.GetMuzzle();
    13.  
    14.         Vector3 gunOffset = gun.localPosition;
    15.  
    16.         Vector3 toTarget = position - transform.position;  
    17.  
    18.         float distance = toTarget.magnitude;
    19.  
    20.         float offsetAngle = Mathf.Atan(-gunOffset.x / distance) * Mathf.Rad2Deg; // x offset to angle offset  
    21.  
    22.         Quaternion lookRotation = Quaternion.LookRotation(toTarget, transform.up) * Quaternion.AngleAxis(-offsetAngle, Vector3.up);  
    23.  
    24.         transform.rotation = Quaternion.RotateTowards(transform.rotation, lookRotation, _soldier.rotationSpeed * Time.deltaTime);
    25.  
    26.         lookingAt = Quaternion.Angle(transform.rotation, lookRotation) < 0.1f;  
    27.  
    28.         if (lookingAt)
    29.         {      
    30.             Ray ray = new Ray(gun.position, gun.forward);
    31.      
    32.             lookingAt = !Physics.SphereCast(ray, 0.15f, distance, 1 <<_soldier.obstaclesLayer);
    33.         }
    34.  
    35.         return lookingAt;
    36.     }
    Note: the removal of transform.rotate, by just using a Quaternion this speed things up quite a bit there seems to be a bit of an engine overhead for using transform.rotate.

    This requires changes to the Example soldiers Update() as they just stop and turn even when they cannot get a shot.

    Code (CSharp):
    1. void Update()
    2.     {
    3.         base.Update();
    4.  
    5.         CheckTimer();
    6.  
    7.         // If we were damaged then look at where we damaged, spray!
    8.         if (damaged)
    9.         {
    10.             if (LookAt(damagedLocation))
    11.             {
    12.                 Shoot();
    13.             }
    14.             else
    15.             {
    16.                 MoveTowards(navAgent.pathGenerated[0]);
    17.             }
    18.             return;
    19.         }
    20.  
    21.         // Check if a path exists
    22.         if (navAgent.pathGenerated.Count > 0)
    23.         {
    24.             // if there's an enemy then dont keep moving, shoot them!
    25.             if (!enemySpotted)
    26.             {
    27.                 // Move in the direction of the next path node
    28.                 MoveTowards(navAgent.pathGenerated[0]);
    29.                 LookAt(navAgent.pathGenerated[0]);
    30.             }
    31.         }
    32.  
    33.         if (soldiersInSight.Count > 0)
    34.         {
    35.             // See if any soldiers in sight are on the enemy team
    36.             IAgent targetSoldier = null;
    37.             for (int i = 0; i < soldiersInSight.Count; i++)
    38.             {
    39.                 if (soldiersInSight[i].GetTeam() != soldier.GetTeam())
    40.                 {
    41.                     targetSoldier = soldiersInSight[i];
    42.                 }
    43.             }
    44.  
    45.             // enemy spotted?
    46.             enemySpotted = targetSoldier != null;
    47.  
    48.             // Any enemy soldiers in sight?
    49.             if (targetSoldier != null)
    50.             {
    51.                 // Look at them, if we are, shoot.
    52.                 if (LookAt(targetSoldier.GetLocation()))
    53.                 {
    54.                     // pew pew
    55.                     Shoot();
    56.                 }
    57.                 else
    58.                 {
    59.                     MoveTowards(navAgent.pathGenerated[0]);
    60.                 }
    61.             }
    62.         }
    63.         else
    64.         {
    65.             // No soldiers so no enemey could be spotted
    66.             enemySpotted = false;
    67.         }
    68.  
    69.         if (soldier.HasFlag() && !returningToSpawn)
    70.         {
    71.             returningToSpawn = true;
    72.  
    73.             navAgent.targetCell = GridManager.instance.FindClosestCell(spawnLocation);
    74.         }
    75.     }
    This found another bug, the FaceOff bug (soldiers move closer to get the shot so sometimes they move next to each other), as bullets don't get their layer allocation until the first update they can actually miss each other at close range. The physics updates offset the bullet outside of the enemy's collider so by the time the layer is set up there is no collision to detect.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Bullet : MonoBehaviour
    6. {
    7.     private IShootable _bulletOwner;
    8.  
    9.     public IShootable bulletOwner {
    10.         get { return _bulletOwner; }
    11.         set
    12.         {
    13.             _bulletOwner = value;
    14.  
    15.             if (_bulletOwner != null)
    16.             {
    17.                 if (_bulletOwner.GetOwner().GetTeam() == Team.Type.A)
    18.                 {
    19.                     gameObject.layer = teamALayer;
    20.                 }
    21.                 else if (_bulletOwner.GetOwner().GetTeam() == Team.Type.B)
    22.                 {
    23.                     gameObject.layer = teamBLayer;
    24.                 }
    25.  
    26.                 shooterPos = _bulletOwner.GetOwner().GetLocation();
    27.  
    28.                 layerChanged = true;
    29.             }
    30.         }
    31.     }
    32.        
    33.  
    34.     public AudioClip bulletSound;
    35.  
    36.     public GameObject bulletCasingObject;
    37.  
    38.     public int poolCasingIndex = 1;
    39.     public int poolIndex = 0;
    40.     public int poolBulletHoleIndex = 2;
    41.     public int poolBloodIndex = 3;
    42.  
    43.     public int damage;
    44.  
    45.     public float speed;
    46.  
    47.     private Rigidbody _rigidbody;
    48.  
    49.     public int teamALayer = 14, teamBLayer = 15;
    50.  
    51.     public Vector2 randomness;
    52.  
    53.     private PoolObject _poolObject;
    54.  
    55.     public bool layerChanged = false;
    56.     public bool ready = false;
    57.  
    58.     public Vector3 shooterPos;
    59.  
    60.     void Awake()
    61.     {
    62.         if (_rigidbody == null)
    63.             _rigidbody = GetComponent<Rigidbody>();
    64.  
    65.         if (!_poolObject)
    66.             _poolObject = GetComponent<PoolObject>();
    67.     }
    68.  
    69.     void OnDisable()
    70.     {
    71.         ready = false;
    72.         bulletOwner = null;
    73.         layerChanged = false;
    74.     }
    75.  
    76.     void OnEnable()
    77.     {
    78.         transform.localEulerAngles = new Vector3(transform.localEulerAngles.x + Random.Range(randomness.x, randomness.y), transform.localEulerAngles.y + Random.Range(randomness.x, randomness.y), transform.localEulerAngles.z);
    79.    
    80.         _rigidbody.angularVelocity = Vector3.zero;
    81.         _rigidbody.velocity = Vector3.zero;
    82.  
    83.         ready = true;
    84.  
    85.         _rigidbody.velocity = (transform.forward * speed);
    86.     }
    87.  
    88.     /*
    89.     void Update()
    90.     {
    91.         if (!ready)
    92.             return;
    93.  
    94.         _rigidbody.velocity = (transform.forward * speed);
    95.  
    96.         if (!layerChanged)
    97.         {
    98.             if (bulletOwner.GetOwner().GetTeam() == Team.Type.A)
    99.             {
    100.                 gameObject.layer = teamALayer;
    101.             }
    102.             else if (bulletOwner.GetOwner().GetTeam() == Team.Type.B)
    103.             {
    104.                 gameObject.layer = teamBLayer;
    105.             }
    106.  
    107.             shooterPos = bulletOwner.GetOwner().GetLocation();
    108.  
    109.             layerChanged = true;
    110.         }
    111.     }
    112.     */
    113.  
    114.     void OnCollisionEnter(Collision collision)
    115.     {
    116.         if (!layerChanged)
    117.             return;
    118.  
    119.         IAgent agent = collision.gameObject.GetComponent<IAgent>();
    120.  
    121.         if (agent != null)
    122.         {
    123.             // Hit soldier
    124.             PoolManager.instance.SetPath(poolBloodIndex);
    125.             PoolObject bloodImpact = PoolManager.instance.Create(null);
    126.             bloodImpact.transform.rotation = Quaternion.FromToRotation(-Vector3.back, collision.contacts[0].normal);
    127.             bloodImpact.transform.position = collision.contacts[0].point + transform.up * -0.04f;
    128.  
    129.             agent.Damage(damage, shooterPos);
    130.         }
    131.         else
    132.         {
    133.             // Hit other  
    134.             PoolManager.instance.SetPath(poolBulletHoleIndex);
    135.             PoolObject bulletHole = PoolManager.instance.Create(null);
    136.             bulletHole.transform.rotation = Quaternion.FromToRotation(-Vector3.back, collision.contacts[0].normal);
    137.             bulletHole.transform.position = collision.contacts[0].point + transform.up * -0.04f;
    138.         }
    139.         _poolObject.Hide();
    140.     }
    141. }
    142.  
    Note: This removes the need for an Update() call as it assigns the layer when the bulletOwner is set. OnEnable sets the velocity.

    Could I suggest the following changes to make this a better learning example and easier to use for Noobs:
    • Code that needs more than one dot in a line could use a better interface e.g. <Class>.instance.<method/variable> could be a static <Class>.<Static Method>.
    • Code that access another classes array elements by index e.g. navAgent.pathGenerated[0] is not good, a getter field e.g. navAgent.netPathPoint would be nicer and easier.
    • Update Pool System to not use the leaky c# linq shorthand, it defeats the object of a Pool when the Pooling system leaks memory (in Unity).
    • The GridMangers Cell Dictionary is also very leaky in Unity and could just be an array.
    If your OK I can work on updating the example with these types of improvements e.g. cleaner/simpler API and GC memory optimisations.

    • Have you considered changing the bullet casings to particles, should be ideal as particles can do world collisions and could even have a Smokey trail renderer.

    As I'm the only contender so far it shouldn't upset anyone!
     
    Last edited: Mar 30, 2017
    Samuel411 likes this.
  35. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    I don't think it would be recommended to change the API right now. Something to keep in mind for my post mortem and future challenges.

    I implemented your changes, thank you for helping with fixes.

    Bullet casings are just the bullet casing prefab I got from the cartoon soldier pack.

    I fixed the toList declarations on the PoolManager and optimized it a tiny bit.

    Thanks :)

    Can't agree with this, other people have said they are working on it and I've already disrupted @DanielQuick 's entry with some proposed changes.
     
    Arowx and Billy4184 like this.
  36. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    Hey Samuel, I found a bug in the handling of the FlagStatusChangedCallback.

    Code (CSharp):
    1. public bool grabbed
    2.     {
    3.         get { return _grabbed; }
    4.         set
    5.         {
    6.             if(_grabbed != value)
    7.                TeamManager.instance.SendFlagMessage(this);
    8.             _grabbed = value;
    9.         }
    10.     }
    In the property grabbed of the Flag class you call first SendFlagMessage and after that you set the field _grabbed to the new value. When we check the status of the flag in FlagStatusChangedCallback it still has the old value. You have to set the field _grabbed first and than fire the event.

    Code (CSharp):
    1. public bool grabbed
    2.     {
    3.         get { return _grabbed; }
    4.         set
    5.         {
    6.             if(_grabbed != value)
    7.             {
    8.                 _grabbed = value;
    9.                 TeamManager.instance.SendFlagMessage(this);
    10.             }
    11.         }
    12.     }
     
    Samuel411 and Arowx like this.
  37. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,302
    The winning code for this challenge is more valuable than any of the prizes.
     
    Arowx likes this.
  38. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    OK here's an overview of how to do it...



    Or at least how they did it for FEAR a game renowned for it's AI.

    In a nutshell it's a two state FSM (Animation* and Goto) with an A*Search used to plan the actions needed based on a 1971 AI approach called STRIPS.

    * Well there is a third called Use Smart Object which just triggers an animation e.g AI Soldier flips table for cover or jumps through window.

    Watch the lecture it's fascinating and well worth the time.
     
  39. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,302
    This guy takes a long time to get to the point...
     
    Samuel411 likes this.
  40. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    It seems dead soldiers still get soldiersInSight filled. That's probably not intended behaviour.
     
  41. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Very handy though for tracking the enemy...
     
  42. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    Lets pretend they have a body camera and everyone on the team can see everyone elses view through their lens lol
     
  43. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    Good catch. Should be implemented in a few minutes gonna make breakfast first :)
     
  44. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    Maybe this is all just a way for me to get people to write me AI code? Mwahahaha...
     
  45. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    As long as I have fun with it, it's ok ;)
     
  46. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    You can always put your copyright on it!
     
  47. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    Shh

    Fixed the flag issue. Had a yummy breakfast, omelette and Pepsi.
     
    GarBenjamin likes this.
  48. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    Holy God, Samuel what did you eat????

    You're not serious about the bugfix, are you?

    Code (CSharp):
    1. _grabbed = value;
    2. if(_grabbed != value)
    3.     TeamManager.instance.SendFlagMessage(this);
    How should _grabbed != value ever be true???

    Maybe you should drink some more coffee!!!
     
  49. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    I worked the last days much on the project and I had a lot of fun to try different approaches and see how the bots win or lose against the examples.

    But I'm thinking I'm stuck a little bit with my ideas because of the lack of different public bots.

    It's more important to me to bring forward this project than to win the challenge :) Therefore I share here my version of the bot so that you have another one to beat.

    My bot is made of different files. Therefore you have to copy the whole folder TeamBlueberry into the scripts folder. The main class is SoldierTeamBlueberry. This is the one which must be attached to the prefab instead of the SoldierExample class

    During the development I had a lot of ideas for nice extensions to bring in some more diversity to the project. Maybe I try my own version of an AI challenge or we can form here a group to work together on it.

    Thanks again, Samuel, for the idea, your effort and your time.
     

    Attached Files:

    Arowx and Samuel411 like this.
  50. Deleted User

    Deleted User

    Guest

    I had my suspicions, haha

    This looks like something I may try out. I don't know much about AI, but I've had a book on Game AI for ages, and up until this week hadn't progressed past the first chapter. Its actually a fairly easy, logical read. I should probably get back to that instead of dinking around with Unity like I have been...