Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

[Community AI Challenge] Tanks! AI Programming Challenge

Discussion in 'General Discussion' started by Arowx, Nov 3, 2015.

  1. DaVeF101

    DaVeF101

    Joined:
    Sep 7, 2014
    Posts:
    134
    Is this still running?... I have a free weekend, so will have a go!
     
  2. Ostwind

    Ostwind

    Joined:
    Mar 22, 2011
    Posts:
    2,804
    I checked the top submitted tanks fighting and theres a lot of sideways strafing going on an almost every time the fight gets stuck both tanks either aiming each other or shooting but not hitting :)
     
  3. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    What do you mean with this secret random deadline? Don't you puplish the end date?
     
  4. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    It has been pointed out that with a published deadline and open code submission the best strategy is to wait, train and perfect your AI then release it just before the entry deadline finishes.

    A secret deadline should help prevent last minute entries that have been trained/tailored to defeat all previous public/open entries.
     
  5. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    Hm, I just wanted to start with my own entry. But if I'm not sure if I have enough time to finish it I don't know if I really should start. Maybe you say tomorrow that the time is over. It would be good to have a hint if we have at least the weekend.
     
  6. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    I generated a random number from 6 to 30 the resultant number is the last day of entry. I can't reveal more than that.

    Note though a simple AI can be written in a matter of minutes.
     
  7. DanielQuick

    DanielQuick

    Joined:
    Dec 31, 2010
    Posts:
    3,137
    A secret deadline really makes me unwilling to participate (will I have time to finish?) and I'm sure many others feel that way too.

    A better solution would be to handle submissions some other way, and for you to publish them (or maybe just the top 10) when the contest is over. Keep the top 10 list updated though so we know if we need to improve.

    Or, because right now it's just for fun we can keep posting them here and the AI will slowly get better as we counter other published AIs. Those who wait until the very end to submit can do so if they want.
     
    Last edited: Nov 6, 2015
    Ostwind likes this.
  8. BornGodsGame

    BornGodsGame

    Joined:
    Jun 28, 2014
    Posts:
    580
    ok, I have seriously wasted too much time on this project. At the moment, I am at a fork in my AI.

    I have the Princess Bride approach, but with that AI I need to know if my opponent is Sicilian or ever served on a pirate ship. My other approach is called the Marvin Offensive, and consists of trying to get the enemy tank to commit suicide.
     
    zombiegorilla and Kiwasi like this.
  9. DanielQuick

    DanielQuick

    Joined:
    Dec 31, 2010
    Posts:
    3,137
    I've made a few small changes and requested them to be merged on GitHub. Hopefully over time (and community involvement!) we can turn this into a pretty neat competition.

    It's odd that only a small part of the project is available on GitHub. What if I wanted to change a script/scene that isn't in the repository? An example being changing the tank prefab's default speed.
     
  10. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Forget the deadline. There are enough users interested in this that the challenge, in some fashion, will probably be a regular thing for a while. Lets just keep running until an unbeatable AI is built, or people stop participating.
     
    zombiegorilla likes this.
  11. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    Finished my entry. The Flying Spaghetti Monster had a 100% pass rate on my end against @Arrowx AI.

    I added code to go around in circle around the tank, I was planning for it to take little attacks randomly while circling the enemy, ended up thinking too hard about it tbh. The map was too small for what I was doing and I was limited in speed, I ended up bumping into walls and having the enemy tank catch up to me rather quickly. What I was doing was better suited to a larger scale map with more variety in speeds. Either way, here we go. I lost a few matches to @DanielQuick AI, mine had a hard time finding his lol and was super boring to watch.

    I ended up making my own ResetMethod since I had no way of extending to the original one lol, maybe something to fix in the future. I also was trying to get access to the health of my enemy so I could swoop in for the kill if the health was below 50, turns out you made it private. A simple readonly could have done well.

    I'm excited to see what others bring.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System;
    4.  
    5. namespace Complete
    6. {
    7.     public class TankAIFSM : TankAI
    8.     {
    9.  
    10.         public bool targetFoundBefore;
    11.         public bool attackMode;
    12.         public bool canAttack = true;
    13.         public bool attackModeCooldownComplete = true;
    14.         public float targetDist;
    15.         public float circleEnemyRadius = 11;
    16.         public float currentAngleOnCircle = 0;
    17.         public float CurrentAngleOnCircle
    18.         {
    19.             get
    20.             {
    21.                 return currentAngleOnCircle;
    22.             }
    23.             set
    24.             {
    25.                 if (currentAngleOnCircle < 0)
    26.                     currentAngleOnCircle = 0;
    27.                 else
    28.                     currentAngleOnCircle = value;
    29.             }
    30.         }
    31.  
    32.         public void Awake()
    33.         {          
    34.             tankName = "Flying Spaghetti Monster";
    35.             moveToMarker.position = RandomMove();
    36.         }
    37.  
    38.         // Your TankAI should mainly be controlled from the Update() function
    39.  
    40.         // Use the tankNav, tankMover and tankShooter to manouver and battle
    41.  
    42.         // tankShooter.m_Fire - true power up / false to shoot
    43.  
    44.         void Update()
    45.         {
    46.             // Reset "method"
    47.             if (target)
    48.                 targetFoundBefore = true;
    49.             if(targetFoundBefore && !target)
    50.             {
    51.                 targetFoundBefore = false;
    52.                 currentAngleOnCircle = 0;
    53.                 circleEnemyRadius = 11;
    54.                 canAttack = true;
    55.                 attackModeCooldownComplete = true;
    56.                 attackMode = false;
    57.                 moveToMarker.position = transform.position;
    58.             }
    59.  
    60.             // Set destination to marker
    61.             tankNav.SetDestination(moveToMarker.position);
    62.  
    63.             // Movements
    64.             if (!attackMode)
    65.             {
    66.                 if (target)
    67.                 {
    68.                     // Make circles around enemy!
    69.                     CurrentAngleOnCircle += 0.5f * Time.deltaTime;
    70.  
    71.                     moveToMarker.position = target.position;
    72.                 }
    73.                 else
    74.                 {
    75.                     // Enemy not found, Lets look for him!
    76.                     if (tankNav.remainingDistance < 2f)
    77.                     {
    78.                         moveToMarker.position = RandomMove();
    79.                     }
    80.                 }
    81.             }
    82.  
    83.             // Find Target!
    84.             if (target == null)
    85.             {
    86.                 target = tankSensor.GetNearestEnemy();
    87.                 return;
    88.             }
    89.  
    90.             Vector3 relative = transform.InverseTransformPoint(target.position);
    91.             float a = Mathf.Atan2(relative.x, relative.z) * Mathf.Rad2Deg;
    92.  
    93.             Vector3 targetDistVector = transform.position - target.position;
    94.             targetDist = targetDistVector.magnitude;
    95.  
    96.             // Attack if we can turn quickly
    97.             // Complete the attack quickly and swiftly then continue
    98.             if (!attackMode && canAttack && attackModeCooldownComplete && targetDist < 15)
    99.             {
    100.                 if(a < 70f)
    101.                 {
    102.                     attackMode = true;
    103.                 }
    104.                 else if(a > -70f)
    105.                 {
    106.                     attackMode = true;
    107.                 }
    108.                 else
    109.                 {
    110.                     attackMode = false;
    111.                 }
    112.             }
    113.          
    114.             // Make sure we can attack
    115.             if(!canAttack)
    116.             {
    117.                 attackMode = false;
    118.             }
    119.  
    120.             // Attack functions
    121.             if (attackMode && targetDist < 15)
    122.             {
    123.                 tankNav.Stop();
    124.                 if (a > 2f)
    125.                 {
    126.                     tankMover.m_TurnInputValue = 0.2f;
    127.                 }
    128.                 else if (a < -2f)
    129.                 {
    130.                     tankMover.m_TurnInputValue = -0.2f;
    131.                 }
    132.                 else
    133.                 {
    134.                     tankMover.m_TurnInputValue = 0f;
    135.                     if (tankSensor.LOS(target))
    136.                     {
    137.                         tankShooter.m_FireButton = !tankShooter.m_FireButton;
    138.                     }
    139.                 }
    140.             }
    141.             else
    142.             {
    143.                 tankNav.Resume();
    144.             }
    145.         }
    146.  
    147.         /// <summary>
    148.         /// Script that returns a position in a circular shape around the inputed Vector
    149.         /// </summary>
    150.         /// <param name="enemyPos">Enemy Position</param>
    151.         /// <returns>Position in circle around EnemyPos</returns>
    152.         Vector3 CircleEnemy(Vector3 enemyPos)
    153.         {
    154.             Vector3 returnVector;
    155.             returnVector.y = transform.position.y;
    156.             // Get X pos on our circle
    157.             returnVector.x = enemyPos.x + circleEnemyRadius * Mathf.Cos(CurrentAngleOnCircle);
    158.             // Get Z pos on our circle
    159.             returnVector.z = enemyPos.z + circleEnemyRadius * Mathf.Sin(CurrentAngleOnCircle);
    160.             // Send the coordinates! FIRE!
    161.             return returnVector;
    162.         }
    163.  
    164.         /// <summary>
    165.         /// Returns a random position around the player's position.
    166.         /// </summary>
    167.         /// <returns>Random Position Near Tank</returns>
    168.         Vector3 RandomMove()
    169.         {
    170.             Vector3 returnVector;
    171.             returnVector = transform.position + UnityEngine.Random.insideUnitSphere * 8;
    172.             returnVector.y = transform.position.y;
    173.             Debug.Log(returnVector);
    174.             return returnVector;
    175.         }
    176.     }
    177. }
    178.  
     
  12. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Yay nice work, you are in 3rd place.

    Scores on the Doors:

    01. 026 - Arowx2
    02. 021 - DanielQuick
    03. 013 - Samuel411
    04. 009 - Arowx
    05. 007 - Carve_Online

    Note: Your tank name has to match your forum username, but you can have it print anything you want.

    FOUND A BUG - HEALTH NOT RESET OOPS!
     
    Last edited: Nov 7, 2015
  13. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
  14. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Bug fixed.

    Should I add a clone wars round, the tanks take on their clones, and up the winners points to 3?

    My thinking is that if you get 3 points for a win and 1 for a draw, tanks that are active will engage each other and one will win gaining them 3 points but static or defensive tanks will tend to draw and only get 2 points (1 point each).
     
    zombiegorilla likes this.
  15. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    3 for a win and 1 for a draw is fairly standard in sporting circles.
     
  16. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Video of current Tanks in action.

     
    Kiwasi likes this.
  17. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System;
    4.  
    5. namespace Complete
    6. {
    7.     public class TankAIZun : TankAI
    8.     {
    9.         private float chargingTime;
    10.  
    11.         public void Awake ()
    12.         {      
    13.             tankName = "Zun";
    14.         }
    15.  
    16.         void Update ()
    17.         {
    18.             Vector3 pos = transform.position;
    19.  
    20.             if (target == null) {
    21.                 target = tankSensor.GetNearestEnemy ();
    22.             }
    23.  
    24.             if (target) {
    25.                 Vector3 enemyPos = target.position;
    26.                 Vector3 fromEnemy = pos - enemyPos;
    27.                 float range = fromEnemy.magnitude;
    28.  
    29.                 Vector3 relative = transform.InverseTransformPoint (enemyPos);
    30.                 float angle = Mathf.Atan2 (relative.x, relative.z) * Mathf.Rad2Deg;
    31.  
    32.                 bool hasLoS = tankSensor.LOS (target);
    33.          
    34.                 tankMover.m_TurnInputValue = Mathf.Clamp (0.1f * Math.Sign (angle) * angle * angle, -1f, 1f);
    35.  
    36.                 if (hasLoS & Mathf.Abs (angle) > 2f) {
    37.                     tankNav.Stop ();
    38.                 } else if (hasLoS) {
    39.                     tankNav.Stop ();
    40.                     if (range > 15f) {
    41.                         chargingTime += Time.deltaTime;
    42.                         float desiredCharging = Mathf.Min (Mathf.Pow ((range - 15f) / 15f, 2.0f), 0.5f);
    43.                         if (chargingTime < desiredCharging) {
    44.                             tankShooter.m_FireButton = true; // Long Range Shots
    45.                         } else {
    46.                             tankShooter.m_FireButton = !tankShooter.m_FireButton;
    47.                             chargingTime = 0f;
    48.                         }
    49.                     } else {
    50.                         chargingTime = 0f;
    51.                         tankShooter.m_FireButton = !tankShooter.m_FireButton; // Short Range Shots
    52.                     }
    53.                 } else {
    54.                     Vector3 goal = target.position - target.forward * 10f;
    55.                     tankNav.SetDestination (goal);
    56.                     moveToMarker.position = goal;
    57.                 }      
    58.             }
    59.  
    60.             if (target == null) { // no target so random move
    61.                 if (tankNav.remainingDistance < 3f) {
    62.                     RandomMove (out moveTo);
    63.                     tankNav.SetDestination (moveTo);
    64.                     moveToMarker.position = tankNav.destination;
    65.                 }
    66.             }    
    67.         }
    68.     }
    69. }
    70.  

    It's rather simple but ohwell. When testing, I noticed some of the damage / hit code being messed up; random misses that end up one shotting you. So I checked the damage code;

    Code (CSharp):
    1.  
    2.         private float CalculateDamage (Vector3 targetPosition)
    3.         {
    4.             // Create a vector from the shell to the target.
    5.             Vector3 explosionToTarget = targetPosition - transform.position;
    6.  
    7.             // Calculate the distance from the shell to the target.
    8.             float explosionDistance = explosionToTarget.magnitude;
    9.  
    10.             // Calculate the proportion of the maximum distance (the explosionRadius) the target is away.
    11.             float relativeDistance = explosionDistance / m_ExplosionRadius;
    12.  
    13.             // Calculate damage as this proportion of the maximum possible damage.
    14.             float damage = relativeDistance * m_MaxDamage;
    15.  
    16.             // Make sure that the minimum damage is always 0.
    17.             damage = Mathf.Max (0f, damage);
    18.  
    19.             return damage;
    20.         }
    Essentially this: float damage = explosionDistance / m_ExplosionRadius * m_MaxDamage;

    So the further away from the tank your shell lands, the more damage. I propose to switch this to something more sensible like

    Code (CSharp):
    1. float damage = (m_ExplosionRadius - explosionDistance) / m_ExplosionRadius * m_MaxDamage;
    So that at range 5,4,3,2,1,0 you deal respectively 0, 20, 40, 60, 80, 100 dmg.
    Current code does at range 5,4,3,2,1,0 respectively 100, 80 ,60, 40, 20, 0.

    (Miss at 5 meters kills you in one hit now; A hit at 0 meter should kill you with my code; but practically will max out at ~80 dmg)

    May want to lower max damage to like 40-70 though, for more pewpew fun.
     
    Kiwasi likes this.
  18. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    @Zuntatos Great find, darn it no wonder my Mr Magoo targeting system was doing so well. Fixing...

    Fixed and updated - re-running current contestants.
     
    Last edited: Nov 7, 2015
  19. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    @Carve_Online I am going to remove my default entry, would you mind if I remove your entry?

    As the number of contestants goes up it follows a square law for the number of rounds needed
    e.g.
    6 tanks, so 30 matches with a maximum of 6, 1-minute rounds or a potential 180 minutes of competition time.

    minus your and my entry

    4 tanks, so 12 matches or potentially 72 minutes.

    Second thoughts your tank is beating mine and we are both still in the top 10.
     
    Last edited: Nov 7, 2015
  20. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    @Zuntatos - Well done your tank is in first place.

    01. 047 Zuntatos
    02. 040 Arowx2 / DanieQuick
    03. 024 Samuel411
    04. 010 Carve_Online
    05. 002 Arowx
     
    Zuntatos likes this.
  21. Ostwind

    Ostwind

    Joined:
    Mar 22, 2011
    Posts:
    2,804
    I agree with this. Hidden or quick deadline probably drives possible participants away. It would be more fun if this would be longer term "contest". If there are a lot of people participating it does not matter much if people tweak and tweak the AI constantly to be better as long no one does things like if enemy.name = arowx then do this.
     
  22. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    I think we need some random start positions judging from the difference between Carve_Online and Arowx (same AI, just one is more vocal).
     
  23. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Interesting concept with no deadline, just an ongoing competition, there is no need to hold off entering. Actually it could be better to enter earlier as the competition should be easier.

    If there are no complaints I think open ended competition it is then. Can you write a competition winning AI and how long can your AI hold the crown. @Zuntatos has the TankAI to beat.

    Maybe the current champion should have a golden tank, not my Sci-Fi tank mind you as that's for my entries. Hey, model your own tank!
     
  24. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Why not? Its simple enough to scramble your own objects name at regular intervals. Scrambling your class name is harder, but doable.

    I'm actually curious to see if this problem has an optimal solution. And how long it takes to find it. But even after we do that there is no reason we can't grab another tutorial and build AIs for that. It could become a long standing community challenge.

    Speaking of challenge, its probably time I start working on my solution.
     
  25. BornGodsGame

    BornGodsGame

    Joined:
    Jun 28, 2014
    Posts:
    580
    Yeah, you can definitely remove my joke one. Sorry I should have stated that earlier. I am going to add a real entry later one when I get it finished which will include the jokes anyway.
    " I would say that was merely a flesh wound, but you didn´t even get close to hitting me´..
    My AI now includes a Princess Bride, Monty Python and Hitchhiker´s Guide reference. How can I lose.
     
    Kiwasi likes this.
  26. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    Updated my AI, going with circular and strafing movements

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System;
    4.  
    5. namespace Complete
    6. {
    7.     public class TankAISA : TankAI
    8.     {
    9.         public string ourTankName = "Samuel411";
    10.         public bool targetFoundBefore;
    11.         public bool targetNowFound = false;
    12.         public bool attackMode;
    13.         public bool canAttack = true;
    14.         public float powerUpTank = 0;
    15.         public float targetDist;
    16.         public float circleEnemyRadius = 20;
    17.         public float currentAngleOnCircle = 0;
    18.         public float CurrentAngleOnCircle
    19.         {
    20.             get
    21.             {
    22.                 return currentAngleOnCircle;
    23.             }
    24.             set
    25.             {
    26.                 if (currentAngleOnCircle < 0)
    27.                     currentAngleOnCircle = 0;
    28.                 else
    29.                     currentAngleOnCircle = value;
    30.             }
    31.         }
    32.  
    33.         public void Awake()
    34.         {
    35.             tankName = ourTankName;
    36.             MoveTo(RandomMove());
    37.         }
    38.  
    39.         void ResetNew()
    40.         {
    41.             targetNowFound = false;
    42.             targetFoundBefore = false;
    43.             canAttack = true;
    44.             attackMode = false;
    45.             currentAngleOnCircle = 0;
    46.             circleEnemyRadius = 20;
    47.             powerUpTank = 0;
    48.             MoveTo(RandomMove());
    49.         }
    50.  
    51.         void FindTarget()
    52.         {
    53.             currentAngleOnCircle = Mathf.Atan2(transform.position.x, transform.position.z) * Mathf.Rad2Deg;
    54.         }
    55.  
    56.         void Update()
    57.         {
    58.             // Reset "method"
    59.             if (target)
    60.                 targetFoundBefore = true;
    61.             if (targetFoundBefore && !target)
    62.             {
    63.                 ResetNew();
    64.             }
    65.  
    66.  
    67.             // Found Target "Method"
    68.             if(!targetNowFound && target)
    69.             {
    70.                 targetNowFound = true;
    71.                 FindTarget();
    72.             }
    73.  
    74.             // Set destination to marker
    75.             tankNav.SetDestination(moveToMarker.position);
    76.  
    77.             // Movements
    78.             if (!attackMode)
    79.             {
    80.                 if (!target)
    81.                 {
    82.                     // Enemy not found, Lets look for him!
    83.                     if (tankNav.remainingDistance < 2f)
    84.                     {
    85.                         MoveTo(RandomMove());
    86.                     }
    87.                 }
    88.             }
    89.  
    90.             // Find Target!
    91.             if (target == null)
    92.             {
    93.                 target = tankSensor.GetNearestEnemy();
    94.                 return;
    95.             }
    96.  
    97.             MoveTo(CircleEnemy(target.position));
    98.             CurrentAngleOnCircle += 15f * Time.deltaTime;
    99.  
    100.             Vector3 relative = transform.InverseTransformPoint(target.position);
    101.             float a = Mathf.Atan2(relative.x, relative.z) * Mathf.Rad2Deg;
    102.  
    103.             Vector3 targetDistVector = transform.position - target.position;
    104.             targetDist = targetDistVector.magnitude;
    105.  
    106.             // Attack if we can turn quickly
    107.             // Complete the attack quickly
    108.             if (!attackMode && canAttack && target)
    109.             {
    110.                 if (a < 70f)
    111.                 {
    112.                     attackMode = true;
    113.                 }
    114.                 else if (a > -70f)
    115.                 {
    116.                     attackMode = true;
    117.                 }
    118.                 else
    119.                 {
    120.                     attackMode = false;
    121.                 }
    122.             }
    123.  
    124.             // Make sure we can attack
    125.             if (!canAttack)
    126.             {
    127.                 attackMode = false;
    128.             }
    129.  
    130.             // Attack functions
    131.             if (attackMode)
    132.             {
    133.                 if (a > 3f)
    134.                 {
    135.                     tankMover.m_TurnInputValue = 1f;
    136.                 }
    137.                 else if (a < 1f)
    138.                 {
    139.                     tankMover.m_TurnInputValue = -1f;
    140.                 }
    141.                 else
    142.                 {
    143.                     tankMover.m_TurnInputValue = 0f;
    144.                     if (tankSensor.LOS(target))
    145.                     {
    146.                         tankShooter.m_FireButton = true;
    147.                         powerUpTank += Time.deltaTime;
    148.                         if (powerUpTank > (targetDist/ (targetDist*targetDist)))
    149.                         {
    150.                             Debug.Log(powerUpTank);
    151.                             powerUpTank = 0;
    152.                             tankShooter.m_FireButton = false;
    153.                         }
    154.                     }
    155.                 }
    156.             }
    157.             else
    158.             {
    159.                 if (a > 2f)
    160.                 {
    161.                     tankMover.m_TurnInputValue = 2f;
    162.                 }
    163.                 else if (a < -2f)
    164.                 {
    165.                     tankMover.m_TurnInputValue = -2f;
    166.                 }
    167.                 else
    168.                 {
    169.                     tankMover.m_TurnInputValue = 0f;
    170.                 }
    171.             }
    172.         }
    173.  
    174.         /// <summary>
    175.         /// Move our Marker to the given Vector3 Position
    176.         /// </summary>
    177.         /// <param name="movePos">Target Move Positiion</param>
    178.         void MoveTo(Vector3 movePos)
    179.         {
    180.             moveToMarker.position = movePos;
    181.         }
    182.  
    183.         /// <summary>
    184.         /// Script that returns a position in a circular shape around the inputed Vector
    185.         /// </summary>
    186.         /// <param name="enemyPos">Enemy Position</param>
    187.         /// <returns>Position in circle around EnemyPos</returns>
    188.         Vector3 CircleEnemy(Vector3 enemyPos)
    189.         {
    190.             Vector3 returnVector;
    191.             returnVector.y = transform.position.y;
    192.             // Get X pos on our circle
    193.             returnVector.x = enemyPos.x + (circleEnemyRadius * Mathf.Cos(currentAngleOnCircle * Mathf.PI/180f));
    194.             // Get Z pos on our circle
    195.             returnVector.z = enemyPos.z + (circleEnemyRadius * Mathf.Sin(currentAngleOnCircle * Mathf.PI / 180f));
    196.             // Send the coordinates! FIRE!
    197.             return returnVector;
    198.         }
    199.  
    200.         /// <summary>
    201.         /// Returns a random position around the player's position.
    202.         /// </summary>
    203.         /// <returns>Random Position Near Tank</returns>
    204.         Vector3 RandomMove()
    205.         {
    206.             Vector3 returnVector;
    207.             returnVector = transform.position + UnityEngine.Random.insideUnitSphere * 20;
    208.             returnVector.y = transform.position.y;
    209.             return returnVector;
    210.         }
    211.     }
    212. }
    213.  
     
  27. Brainswitch

    Brainswitch

    Joined:
    Apr 24, 2013
    Posts:
    270
    I think the whole movement thing is just weird... What are the rules? Are you allowed to use the NavMeshAgent to strafe? Are you allowed to use values above 1f (or below -1f) in tankMover turn and movement input?
    Shouldn't either the NavMeshAgent or the TankMover move the tanks?
     
    Kiwasi likes this.
  28. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    @Brainswitch Yeah I agree, it's a bit dubious what you can and can't do.

    Personally I only used the agent.SetDestination and setting the rotation input to -1, 1 (which I consider natural for user input).

    Just checked, original script uses -0.2, 0.2 and Samuel uses -2, 2

    The rotation input seems to go directly into rotation code without any clamping / limiting, so we could just set it directly to the value and turn in 1 frame. @Arowx Help!
     
  29. Brainswitch

    Brainswitch

    Joined:
    Apr 24, 2013
    Posts:
    270
    Combined with the movement input you could almost teleport to any position (if there are no obstacles in the way) in 1 frame.


    What about introducing some sort of energy/fuel, it automatically regenerates but everything costs energy. Turning, moving, shooting etc. Turning slowly wouldn't drain the energy (much/if at all/lower than the regeneration) but turning quickly would. Same with movement and charging weapons. Thoughts?
     
    ChrisSch likes this.
  30. DanielQuick

    DanielQuick

    Joined:
    Dec 31, 2010
    Posts:
    3,137
    The original script (before the AI change) uses Input.GetAxis so [-1,1] is the proper expected range. It certainly needs to be clamped. I'll make the changes and suggest the merge.

    In my opinion, it would be nice to have the competition have start/finish dates. That way we can update the project in larger ways without breaking existing AIs.

    The start of the competition would provide the base project (as is done here with the .zip) which wouldn't change throughout the competition barring necessary bug-fixes.

    Every new competition could use the most up-to-date scripts and possibly a new twist thrown in the mix (fuel, power ups, etc) to keep things fresh.
     
  31. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    I'm thinking we should have some kind of API to move and rotate the tank. So that we have a defined set of methods which accept only values in defined ranges. At the moment you can use either the NavMeshAgent or the variables of the orginal TankMovement script and set them to impossible values.

    Maybe we can use this first competition also as a base to see what we can make better or what other features we would like to have. E.g. I would like to have a rotateable turret so that the tank can move in one direction and shot in another ;-)
     
    Kiwasi likes this.
  32. Samuel411

    Samuel411

    Joined:
    Dec 20, 2012
    Posts:
    646
    My bad, I didn't know what exactly the max and min of the turning speed should've been. I thought the limit was 0.2 since that's what the original had and what DanielQuick used in his turret, then I saw Arowx used 1 so I just set it to 2 :p either way that was the rotation speed when attackMode was off so it doesn't make much of a difference in my AI, here's a fix o.o
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System;
    4.  
    5. namespace Complete
    6. {
    7.     public class TankAISA : TankAI
    8.     {
    9.         public string ourTankName = "Samuel411";
    10.         public bool targetFoundBefore;
    11.         public bool targetNowFound = false;
    12.         private bool foundTargetTime;
    13.         public bool attackMode;
    14.         public bool canAttack = true;
    15.         public float powerUpTank = 0;
    16.         public float targetDist;
    17.         public float circleEnemyRadius = 20;
    18.         public float currentAngleOnCircle = 0;
    19.         public float CurrentAngleOnCircle
    20.         {
    21.             get
    22.             {
    23.                 return currentAngleOnCircle;
    24.             }
    25.             set
    26.             {
    27.                 if (currentAngleOnCircle < 0)
    28.                     currentAngleOnCircle = 0;
    29.                 else
    30.                     currentAngleOnCircle = value;
    31.             }
    32.         }
    33.  
    34.         public void Awake()
    35.         {
    36.             tankName = ourTankName;
    37.             MoveTo(RandomMove());
    38.         }
    39.  
    40.         void ResetNew()
    41.         {
    42.             targetNowFound = false;
    43.             targetFoundBefore = false;
    44.             canAttack = true;
    45.             attackMode = false;
    46.             currentAngleOnCircle = 0;
    47.             circleEnemyRadius = 20;
    48.             powerUpTank = 0;
    49.             MoveTo(RandomMove());
    50.         }
    51.  
    52.         void FindTarget()
    53.         {
    54.             currentAngleOnCircle = Mathf.Atan2(transform.position.x, transform.position.z) * Mathf.Rad2Deg;
    55.         }
    56.  
    57.         void Update()
    58.         {
    59.             // Reset "method"
    60.             if (target)
    61.                 targetFoundBefore = true;
    62.             if (targetFoundBefore && !target)
    63.             {
    64.                 ResetNew();
    65.             }
    66.  
    67.  
    68.             // Found Target "Method"
    69.             if(!targetNowFound && target)
    70.             {
    71.                 targetNowFound = true;
    72.                 FindTarget();
    73.             }
    74.  
    75.             // Set destination to marker
    76.             tankNav.SetDestination(moveToMarker.position);
    77.  
    78.             // Movements
    79.             if (!attackMode)
    80.             {
    81.                 if (!target)
    82.                 {
    83.                     // Enemy not found, Lets look for him!
    84.                     if (tankNav.remainingDistance < 2f)
    85.                     {
    86.                         MoveTo(RandomMove());
    87.                     }
    88.                 }
    89.             }
    90.  
    91.             // Find Target!
    92.             if (target == null)
    93.             {
    94.                 target = tankSensor.GetNearestEnemy();
    95.                 return;
    96.             }
    97.  
    98.             MoveTo(CircleEnemy(target.position));
    99.             CurrentAngleOnCircle += 13f * Time.deltaTime;
    100.  
    101.             Vector3 relative = transform.InverseTransformPoint(target.position);
    102.             float a = Mathf.Atan2(relative.x, relative.z) * Mathf.Rad2Deg;
    103.  
    104.             Vector3 targetDistVector = transform.position - target.position;
    105.             targetDist = targetDistVector.magnitude;
    106.  
    107.             // Attack if we can turn quickly
    108.             // Complete the attack quickly
    109.             if (!attackMode && canAttack && target)
    110.             {
    111.                 if (a < 70f)
    112.                 {
    113.                     attackMode = true;
    114.                 }
    115.                 else if (a > -70f)
    116.                 {
    117.                     attackMode = true;
    118.                 }
    119.                 else
    120.                 {
    121.                     attackMode = false;
    122.                 }
    123.             }
    124.  
    125.             // Make sure we can attack
    126.             if (!canAttack)
    127.             {
    128.                 attackMode = false;
    129.             }
    130.  
    131.             // Attack functions
    132.             if (attackMode)
    133.             {
    134.                 if (a > 1)
    135.                 {
    136.                     tankMover.m_TurnInputValue = 1f;
    137.                 }
    138.                 else if (a < -3f)
    139.                 {
    140.                     tankMover.m_TurnInputValue = -1f;
    141.                 }
    142.                 else
    143.                 {
    144.                     tankMover.m_TurnInputValue = 0f;
    145.                     if (tankSensor.LOS(target))
    146.                     {
    147.                         tankShooter.m_FireButton = true;
    148.                         if (!foundTargetTime)
    149.                         {
    150.                             foundTargetTime = true;
    151.                             powerUpTank = Time.time + TargetPower(targetDist);
    152.                         }
    153.                         if (Time.time > powerUpTank)
    154.                         {
    155.                             powerUpTank = 0;
    156.                             foundTargetTime = false;
    157.                             tankShooter.m_FireButton = false;
    158.                         }
    159.                     }
    160.                 }
    161.             }
    162.             else
    163.             {
    164.                 powerUpTank = 0;
    165.                 if (a > 2f)
    166.                 {
    167.                     tankMover.m_TurnInputValue = 1f;
    168.                 }
    169.                 else if (a < -2f)
    170.                 {
    171.                     tankMover.m_TurnInputValue = -1f;
    172.                 }
    173.                 else
    174.                 {
    175.                     tankMover.m_TurnInputValue = 0f;
    176.                 }
    177.             }
    178.         }
    179.  
    180.         /// <summary>
    181.         /// Gets how much power we should give the projectile before shooting it based on enemy distance
    182.         /// </summary>
    183.         /// <param name="dist">The distance from the target</param>
    184.         /// <returns>Power Target Needed</returns>
    185.         float TargetPower(float dist)
    186.         {
    187.             float targetPower = 0;
    188.             targetPower = (dist * 2) / (dist * dist);
    189.             targetPower = targetPower / tankShooter.m_MaxChargeTime;
    190.             targetPower = targetPower * 3f;
    191.             return targetPower;
    192.         }
    193.  
    194.         /// <summary>
    195.         /// Move our Marker to the given Vector3 Position
    196.         /// </summary>
    197.         /// <param name="movePos">Target Move Positiion</param>
    198.         void MoveTo(Vector3 movePos)
    199.         {
    200.             moveToMarker.position = movePos;
    201.         }
    202.  
    203.         /// <summary>
    204.         /// Script that returns a position in a circular shape around the inputed Vector
    205.         /// </summary>
    206.         /// <param name="enemyPos">Enemy Position</param>
    207.         /// <returns>Position in circle around EnemyPos</returns>
    208.         Vector3 CircleEnemy(Vector3 enemyPos)
    209.         {
    210.             Vector3 returnVector;
    211.             returnVector.y = transform.position.y;
    212.             // Get X pos on our circle
    213.             returnVector.x = enemyPos.x + (circleEnemyRadius * Mathf.Cos(currentAngleOnCircle * Mathf.PI/180f));
    214.             // Get Z pos on our circle
    215.             returnVector.z = enemyPos.z + (circleEnemyRadius * Mathf.Sin(currentAngleOnCircle * Mathf.PI / 180f));
    216.             // Send the coordinates! FIRE!
    217.             return returnVector;
    218.         }
    219.  
    220.         /// <summary>
    221.         /// Returns a random position around the player's position.
    222.         /// </summary>
    223.         /// <returns>Random Position Near Tank</returns>
    224.         Vector3 RandomMove()
    225.         {
    226.             Vector3 returnVector;
    227.             returnVector = transform.position + UnityEngine.Random.insideUnitSphere * 20;
    228.             returnVector.y = transform.position.y;
    229.             return returnVector;
    230.         }
    231.     }
    232. }
    233.  
     
  33. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    OK I'm in the process of merging DanielQuicks fork which has a clamp on turn input and a wrapper for the NavMeshAgent.

    Also adding random spawn points.

    Also halving the explosive force of shells as it was knocking back the tanks so far the shells would cause minimal damage.
     
    Last edited: Nov 7, 2015
  34. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    @Zuntatos - Still First

    01. 038 Zuntatos
    02. 027 Samuel411 / Arowx2
    03. 014 DanieQuick
    04. 003 Arowx

    Good work @Samuel411 looking good.

    Updated version with random spawn points.

     
  35. darkhog

    darkhog

    Joined:
    Dec 4, 2012
    Posts:
    2,218
    There are few games where you code ai to fight other ais. Can't name any, but I know there are some such games. As for participating, I'd love to, but not now since I want to keep fresh mind and not get used to Unity's navmesh since it'll be useless in game I'm working on (which would require dynamic navmesh generation) to code AI for my games.
     
  36. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    :confused:DOH! Found and fixed new bug. I've moved the shells to their own physics layer as they were blocking the LOS check.

    Running competition again. :oops::oops::rolleyes:;)

    01. 038 Samuel411
    02. 032 Arowx2
    02. 028 Zuntatos
    03. 020 DanieQuick
    04. 004 Arowx
     
    Last edited: Nov 8, 2015
    Samuel411 likes this.
  37. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    OK, ran longer running test, 10 rounds to win or a 20 limit.

    And the good news is the results match the above.

    01. 130 Samuel411
    02. 119 Arowx2
    02. 104 Zuntatos
    03. 062 DanieQuick
    04. 030 Arowx
     
    Samuel411 likes this.
  38. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Just browsing the code before preparing my own solution. Couple of points (I might do a pull request if I get time).

    Currently calling TankSensor.GetNearestEnemy does a LOS check. But it returns a Transform. This means once you have seen a tank you have a permanent reference to it. This makes stealth or hiding totally worthless. It would open up the possibility space a bit, and also be more robust, if it simply returned position and rotation data wrapped up in a strict. (I realise this would be a breaking change for existing AI).

    It would be nice to be able to read (not write) TankShooting.m_CurrentLaunchForce. I realise I could calculate it, but that seems a bit pointless. Again this opens up the possibility space to aim shots more precisely.

    TankMovement.m_MovementInputValue looks like it should be public? It also looks like it and TankMovement.m_TurnInputValue should be clamped between -1 and 1 in their setters.

    Read access to TankHealth.m_CurrentHealth would be nice.
     
  39. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    8,984
    Ongoing is good. Maybe something like weekly official battles, and the winner of that battle has the gold tank until the the next week.
     
    Samuel411 and Martin_H like this.
  40. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    @Arowx is the project in OP the most up to date one with random spawns etc?
     
  41. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    It should be if the level has about 10+ spawn points and the ChallengeManager has a populated list of them.
     
  42. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    I found a bug in the TankSensor. The list enemyTanksList contains often the same object twice. Than the GetNearestEnemy method uses the LOS method to deliver the nearest enemy. But If the enemy isn't in the line of sight the method delivers null.

    I'm thinking it would be good if you check in the OnTriggerEnter method first if the enemy is already in the list like in the following code:

    Code (CSharp):
    1.  
    2.   public void OnTriggerEnter( Collider other )
    3.   {
    4.     if ((other.transform != transform) &&
    5.         !enemyTanksList.Contains(other.transform))
    6.     {
    7.         enemyTanksList.Add(other.transform);
    8.     }
    9.   }
    10.  
    11.  
     
  43. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    I have two other improvements:
    1) The Reset method of TankAI should be virtual so that we can implement our own Reset method
    2) I need the ResetPath method of the navMeshAgent. This method should be accessible also with the TankNavigator.
     
  44. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    Ok, here is my entry to the challenge:

    Code (CSharp):
    1. using UnityEngine;
    2. using System;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using UnityEngine.Assertions;
    6.  
    7. namespace Complete
    8. {
    9.     public class TankAITH : TankAI
    10.     {
    11.         enum TankAITHState
    12.         {
    13.             None,
    14.             Search,
    15.             Orbit,
    16.             Fight
    17.         }
    18.  
    19.         TankAITHState currentState;
    20.         float distancteToTarget = -1;
    21.  
    22.         float[] distanceToTime = new float[11] { 0.01f, 0.08f, 0.15f, 0.22f, 0.29f, 0.36f, 0.43f, 0.5f, 0.62f, 0.69f, 0.75f };
    23.  
    24.         List<Vector3> path = new List<Vector3>() { new Vector3(25.0f, 0f, 25.0f), new Vector3(-25.0f, 0f, 25.0f), new Vector3(-25.0f, 0f, -20.0f), new Vector3(25.0f, 0f, -25.0f) };
    25.         int currentPathIdx = -1;
    26.  
    27.         float sensorMaxDistance;
    28.         float timeStarted;
    29.         float reloadingTime;
    30.  
    31.         float nextAngle = 0;
    32.         float lastRemainingDistance;
    33.  
    34.         NavMeshAgent navMeshAgent;
    35.  
    36.         protected override void SetName()
    37.         {
    38.             tankName = "THoeppner";
    39.         }
    40.  
    41.         void Start()
    42.         {
    43.             navMeshAgent = GetComponent<NavMeshAgent>();
    44.             Assert.IsNotNull(navMeshAgent, "NavMeshAgent isn't set");
    45.             navMeshAgent.stoppingDistance = 1.0f;
    46.             sensorMaxDistance = tankSensor.GetComponent<SphereCollider>().radius;
    47.             tankShooter.m_FireButton = false;
    48.             currentState = TankAITHState.Search;
    49.         }
    50.  
    51.  
    52.         // Your TankAI should mainly be controlled from the Update() function
    53.  
    54.         // Use the tankNav, tankMover and tankShooter to manouver and battle
    55.  
    56.         // tankShooter.m_Fire - true power up / false to shoot
    57.  
    58.         void Update()
    59.         {
    60.             distancteToTarget = -1;
    61.             target = tankSensor.GetNearestEnemy();
    62.             if (target)
    63.             {
    64.                 distancteToTarget = (target.position - transform.position).magnitude;
    65.                 if (tankSensor.LOS(target))
    66.                 {
    67.                     currentState = TankAITHState.Fight;
    68.                 }
    69.                 else
    70.                 {
    71.                     if (currentState != TankAITHState.Orbit)
    72.                     {
    73.                         tankNav.Stop();
    74.                         navMeshAgent.ResetPath();
    75.                     }
    76.                     currentState = TankAITHState.Orbit;
    77.                 }
    78.             }
    79.             else
    80.             {
    81.                 if (currentState != TankAITHState.Search)
    82.                 {
    83.                     tankNav.Stop();
    84.                     navMeshAgent.ResetPath();
    85.                     currentPathIdx = -1;
    86.                 }
    87.                 currentState = TankAITHState.Search;
    88.             }
    89.  
    90.             switch (currentState)
    91.             {
    92.                 case (TankAITHState.Fight):
    93.                     FightEnemy();
    94.                     break;
    95.  
    96.                 case (TankAITHState.Orbit):
    97.                     OrbitEnemy(target.position);
    98.                     break;
    99.  
    100.                 case (TankAITHState.Search):
    101.                     SearchEnemy();
    102.                     break;
    103.             }
    104.         }
    105.  
    106.         private void SearchEnemy()
    107.         {
    108.             tankMover.m_TurnInputValue = 0f;
    109.  
    110.             if (navMeshAgent.hasPath)
    111.             {   // Already a path set
    112.                 return;
    113.             }
    114.  
    115.             if (currentPathIdx < 0)
    116.             {
    117.                 float distance = float.MaxValue;
    118.                 for (int idx = 0; idx < path.Count; idx++)
    119.                 {
    120.                     if ((path[idx] - transform.position).sqrMagnitude < distance)
    121.                     {
    122.                         currentPathIdx = idx;
    123.                     }
    124.                 }
    125.             }
    126.  
    127.             Vector3 moveTo = path[currentPathIdx];
    128.  
    129.             tankNav.SetDestination(moveTo);
    130.             currentPathIdx++;
    131.             currentPathIdx = currentPathIdx % path.Count;
    132.         }
    133.  
    134.         private void FightEnemy()
    135.         {
    136.             tankNav.Stop();
    137.             navMeshAgent.ResetPath();
    138.  
    139.             if (reloadingTime > Time.time)
    140.             {
    141.                 return;
    142.             }
    143.  
    144.             // Get the vector to the enemy
    145.             Vector3 relative = transform.InverseTransformPoint(target.position);
    146.             float a = Mathf.Atan2(relative.x, relative.z) * Mathf.Rad2Deg;
    147.  
    148.             if (a > 1f)
    149.             {   // Vector large than 1 degree
    150.                 tankMover.m_TurnInputValue = 1f;
    151.             }
    152.             else if (a < -1f)
    153.             {   // Vector large than 1 degree
    154.                 tankMover.m_TurnInputValue = -1f;
    155.             }
    156.             else
    157.             {   // Fire
    158.                 tankMover.m_TurnInputValue = 0f;
    159.                 if (!tankShooter.m_FireButton)
    160.                 {
    161.                     timeStarted = Time.time;
    162.                 }
    163.                 tankShooter.m_FireButton = true;
    164.             }
    165.  
    166.             if (tankShooter.m_FireButton)
    167.             {   // Check how long we have to hold down the fire button
    168.                 int idx = (int)((distancteToTarget / sensorMaxDistance) * distanceToTime.Length) - 1;
    169.                 idx = Mathf.Clamp(idx, 0, distanceToTime.Length - 1);
    170.  
    171.                 if (Time.time > (timeStarted + distanceToTime[idx]))
    172.                 {   // Release the button and wait till we have reloded
    173.                     tankShooter.m_FireButton = false;
    174.                     reloadingTime = Time.time + 0.5f;
    175.                 }
    176.             }
    177.         }
    178.  
    179.         void OrbitEnemy(Vector3 enemyPos)
    180.         {
    181.             tankMover.m_TurnInputValue = 0f;
    182.  
    183.             if (navMeshAgent.hasPath)
    184.             {   // Already a path set
    185.                 if (Mathf.Approximately(tankNav.remainingDistance, lastRemainingDistance))
    186.                 {
    187.                     nextAngle += 10;
    188.                 }
    189.                 else
    190.                 {
    191.                     lastRemainingDistance = tankNav.remainingDistance;
    192.                     return;
    193.                 }              
    194.             }
    195.  
    196.             Vector3 to = transform.position - enemyPos;
    197.             float distance = distancteToTarget - 1;
    198.             float angle = Vector3.Angle(to, Vector3.right);
    199.             if (to.z < 0) { angle = 360 - angle; }
    200.             angle = (angle - nextAngle) * Mathf.Deg2Rad;
    201.  
    202.             Vector3 moveTo = enemyPos + new Vector3(Mathf.Cos(angle), 0f, Mathf.Sin(angle)) * distance;
    203.  
    204.             if (tankNav.SetDestination(moveTo))
    205.             {
    206.                 nextAngle = 10;
    207.             }
    208.             lastRemainingDistance = tankNav.remainingDistance;
    209.         }
    210.     }
    211.  
    212. }
    213.  
     
  45. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    One question in principle: If you look at Samuels entry you see that his tank can move sideways! On this way the tank has a big advantage. I'm thinking such a movement shouldn't be allowed. What do you think?
     
  46. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194


    What do you think of this for a level concept, it's a work in progress at the moment.

    Good find @THoeppner added will updated in a bit.
     
    peteorstrike likes this.
  47. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Well, my tank Arowx2 uses sideways movement, what is really needed is a rotatable tank turret.
     
  48. THoeppner

    THoeppner

    Joined:
    Oct 10, 2012
    Posts:
    205
    Great concept. Looks like we will have Tron Tanks soon ;-)

    If you need some help with the API (things like the turret etc) maybe I can help you. Sounds like an interesting task to create such an interface so that we can create our AIs with it.
     
  49. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    @THoeppner not bad for first try 2nd place

    01. 062 Samuel411
    02. 046 THoeppner
    03. 037 Arowx2
    04. 023 DanieQuick / Zuntatos
     
    Last edited: Nov 9, 2015
  50. ChrisSch

    ChrisSch

    Joined:
    Feb 15, 2013
    Posts:
    763
    Fun game, following. :)
    I like @Brainswitch 's idea of all actions costing energy, and energy recharging over time (a couple sec). That way turn speed can be what you want and cost depending on it, and you can't spam bullets. :D