The Regen3 Project (A football game development diary)
I'm getting a lot of enjoyment from the two python development logs, so I thought I'd contribute my own here. Besides, FOFC has a lot of quality insight and thoughts about football sims in general, so I'll probably get a lot of great feedback.
(plus, I only have 1 member on my own forum! ;) )
The first few posts in this topic are going to be re-posts of previous work, just to help build some context for the current work. I'll date the historical info so you'll know where it was in the process.
Also, I'll be happy to hear feedback as we go. There are some aspects of the game that are locked in, but most of it can be changed based on your impressions.
Here's a quick description of the goals for The Regen3 Project:
Other than that, the rest is up for change. In the end, I basically want to try and take the game of football, and make the rosters more intimate. (like in the NBA) I want every player to mean something to you. I want it to be graphical because I have a hard time getting a feeling for my players unless I can see them perform. Some of my fondest sports sim memories are from College Hoops 2k8, when I'd just watch my players play games. I still remember my wild SF who would never run his plays right, but was always running mad around the court disrupting the other team and slamming down breakaways.
I want to recreate that experience in a football game.
The play engine is starting to take shape. I finally have it set up so I can create plays 'offline' and then copy-paste them into the game at a later date. It took a while, but now I can make offensive and defensive plays without having my development computer with me. (of course I mean at work :) )
For the game engine itself, it has some basic features running:
Players can have varying speed, quickness and strength. (So far that's all the ratings I need)
Plays are imported from the playbook prior to starting the play. (not hard-coded anymore)
Formations kind of work
Pass routes work with up to 4 route segments
Man coverage works, but it limited to tight coverage over the top. AI structure in place to add more man coverages, though.
Formations kind of work
I've uploaded a YouTubes to show what it looks like: Regen3 Update#1: First Play - YouTube
(I'm still figuring out how to do the YouTubes well, so be patient with the quality!)
You might notice that every player is Ronnie or Green Ronnie (from Questionable Hero). That's just because I don't have a great football player model yet. I've been looking, and have found a couple that show promise, but we're not quite there yet. Once I find a model that is the right style, and has the ability to change uniforms, I'll make the changes in the game. For now, we'll all have to live with my random Questionable Hero assets. :D
Pass Blocking & Pass Rushing
More formations work
Pass Rushing and Pass Protection
I think I have a pretty good pass rush and pass protection system now. (video coming soon) There isn't a lot of complexity yet, since every pass rusher is using speed to try and get around the OL. It's fun to watch, though. Depending on the way the DL is getting blocked, they either try to speed rush around the outside, or pull a cutback move.
I'm going to let any new techniques wait until I get the other parts of the game engine working.
While doing the pass rushing and block update, I made some pretty cool changes to the AI. I have to roll those updates out to the route running and man coverage next. The AI update is going to allow for some better real-time decision making for both WR (think run-and-shoot!) and the DB (switching, reading the QB, breaking off coverage to get INT...)
Overall, it's going to be pretty sweet.
Roll out AI updates to WR/DB
Start QB stuff
Check out the YouTube channel for the latest update video!
In this update I show the latest progress on Pass Rush, Pass Block, Zone Defense, and Passing.
Regen3 Update#2: Progress Update - YouTube
It's all still rough, but a lot of the systems are starting to work together pretty well. Now if I could only find a better player model than the Questionable Hero's Ronnie model.......
From 7/20/2016 through 7/23/2016
Today is all about updating the QB actions and AI.
The QB just stands there, and does nothing. I type in the WR number that I want him to throw the ball to and press a button to make the ball launch. Essentially, the QB is just standing there to look pretty.
Goal For Today:
1. The QB will get a receiver progression (defined by the play loaded) and check through each WR to evaluate if they are open
2. QB pick the best WR option
3. QB read the WR movement and decide where to place the throw
4. QB make the throw
5. I just sit back and watch
Let's see how much of this I can get done in 1 day.....
So that didn't quite happen....
Most of it was nearly complete, but then I created a bug that wouldn't allow me to proceed for nearly 3 hours. When I figured it out, there was one of those "omg I'm dumb..." moments.
I think I should be able to fix everything now that the bug is squashed. Let's aim for today to have this thing running!
So here's why I'm dumb.
I wrote some beautiful and amazing "read the defense" logic (that doesn't work) implemented it, and hit go...
The problem is that according to my QB logic, every WR is open at the microsecond the ball is snapped. The QB whips the ball out every play the moment I hit go.
That's just dumb.
Then I added an internal clock to the QB, to simulate the idea that he has to wait for the play to develop, but the clock never worked. It took me hours to figure out why.
Answer: because every WR was open the second the ball is snapped, the AI never got to the second level to activate the QB clock. The QB saw the WR was open, and then proceeded to the throwing AI.
Simple solution, just swap the QB clock to make it in front of the defense reading!
Cool! Now the QB waits the right amount of time for the play to develop, and then scans the field! It worked..... only now the defense had time to properly cover the WRs.
No problem, the QB can just wait until one gets open... only he doesn't.
If there isn't an open WR the second the QB clock goes off, the QB just hangs on to the ball forever.
I think I have the solution, but it's going to take another 2 hours. I'll get a video up when it's done to show the progress.
Basic QB reading done! That got a lot more complicated than I thought it would.
Video of it in action here: Regen3 Update#3: Passing Updates - YouTube
From 7/25/2016 through 7/28/2016
After playing round with the WR reading AI, I think it's in a good enough place to continue on. (not ideal, but at MVC level)
The next hit-list of items to work on all revolve around the passing game:
- Pass accuracy (let's get the ball at least in the neighborhood of the receiver!)
- Different throw "styles" (lob, bullet, floater...)
I don't think this should take too long, but you never know....
It was surprisingly difficult, but I got the passing power equations figured out.
The engine is mostly physics based, so I'm using a force to send the ball flying. This causes some issues....
If I want to hit a spot somewhere on the field, I need to know:
- The direction vector the spot is from the QB (easy to figure out)
- The distance the spot is from the QB (easy to figure out)
- The height/lob to throw the ball (this is an input, based on the throw style)
- The force to apply to the ball (really really hard to figure out!)
So the force not only varies based on the distance, but also the lob I'm going to throw the ball at. If I fix the height/lob, it's easy. However, I want to be able to change the lob based on the QB style, so I have to account for that somehow and still hit the dot for every combination of lob and distance.
That's where the equation got weird.
Throw in the fact that I'm not using normal gravity (to make the ball flight more visually pleasing) and I've added air friction (to have a nicer trajectory at the end of the pass), and you have a recipe for fried brain.
I think I have it now, though. I can move the spot all around the field and hit it every time. (tested from 5 yards to 65 yards)
Insert this into the AI that leads a running receiver, and we might have something cool!
Passing accuracy update video on the YouTubes now: Regen3 Update #4: Pass Accuracy - YouTube
From 7/29/2016 through 8/1/2016
Today is going to be all about updating the player models and field. I'd like to accomplish 2.5 things today:
1. Size the players appropriately to the field/coordinate system
2. Put some yard lines on the field to help with play testing
.5 Hopefully get an actual football player model to replace the Ronnie model!
Updated graphic pics!
Yes, the QB is wearing the football.
On the topic of ratings....
I've spent the last few days working with the ratings for player movement. (speed, acceleration, agility) As I was fine-tuning everything, a thought came to me. Is the current system of describing how a player runs more complicated than we need?
Think about Madden, since most football games seem to use the Madden style of running ratings.
SPD: Describes the top-end speed a player can get to
ACC: Describes how quickly a player gets to top speed
AGI: Something about turning. I've never really understood how it's implemented in Madden, but in other games it controls how fast you can be running before making a turn. (in other words, how much do you need to slow down before making a turn)
Looking at that list, I think that accurately describes everything we want to know about how a player runs, and I could see all sorts of interesting decisions it leads to. Do I want the speedy WR that has low AGI? Will he be too slow in and out of cuts? Does the CB have high enough ACC to cover the short routes?
However, in practice, how many of us can actually see the difference between these two players:
90 ACC 70 AGI
70 ACC 90 AGI
We all know the difference between a 90 SPD and 70 SPD guy, but how much do the AGI/ACC ratings really matter? Does one matter more than the other? Do they average eachother out? (this goes for every sports game with this rating system)
I have no idea, and I've been player these games for over 20 years.
Now fast forward to the draft. What's the first thing you sort on when drafting any running type position? I bet it's SPD. (ok, for me it's often height. But that's a different rant of mine ;) )
ACC and AGI are sometimes a tiebreaker, but we almost all go for SPD.
So that's my long-winded way of proposing that the Regen3 Project combine all 3 ratings into a single speed rating. I've done some testing (video to be posted soon) and have found that the best distribution of running across a 0-100 rating system comes from a combined single speed rating.
Here are some other interesting findings:
--When acceleration is added as a 0-100 rating, you'll find that high-speed players get bunched at the top. You get "groups" where anyone with speed in the 80-100 range all look about the same. Lower than 80, the players tend to spread out until you get to the bottom 0-20 speed guys, where they are so slow that it almost ruins the engine. I think this might be the same thing seen in the Madden engine, which might be why you rarely see players with lower than 40 speed. (measured in a 40-yard dash)
--When acceleration is locked in the middle, the distribution of players at the low end of speed looks better, but the high end speed players all stay bunched. Basically all the decision making is for players below 80 speed, because all players above 80 are almost the same. That's not what we want. If anything, we want the opposite.
--When acceleration is locked at the max possible, the distribution across speed is nice but everyone is far too fast in change-of-direction situations. It looks too unrealistic. (measured in a 5-10-5 shuttle run) This would affect how the d-line and o-line play. Pass rushing is going to be a nightmare because the o-line is always reading the defender, and is at a disadvantage.
--Agility works as you'd think, but makes the players look stupid at the low end of the rating. On a 0-100 scale, any agility rating near 0 has players slowing down so much to make turns, that it is just dumb.
--When I limited the low end, to keep speed up during turns, the agility rating quickly became meaningless. (all players turned at a reasonable rate) There's a happy medium, but I think the agility rating is so much lower impact than the speed rating, it doesn't make sense to keep it.
Ultimately what I went with was a speed rating that has the AGI rating set at the max, and the ACC rating at about 80% max. The running distribution in the 40 seems linear across all ratings, and the 5-10-5 shuttle is pretty close to linear. It also works with all SPD ratings from 0-100.
Of course, this is always going to be up for future improvement as the game engine progresses...
Finally got the refinements done on the passing engine to account for the new ratings and the new player model & field size.
You wouldn't believe how hard it was to get the ball to hit the WR on every route, at every distance, with every potential WR speed, at every lob height!
In the end, I resorted to my trusty physics books and modeled a bunch of the trajectory/forces/gravity formulas. (who knew that stuff would be useful some day??)
Stay in school, kids!
In the end, the updates were worth the effort. The passing accuracy is almost 100% dead on for every throw. (some minor issues exist with hitting the WR at the knees instead of the chest, on some routes when the WR is at 100 speed)
Now we don't want 100% accurate QBs all the time, so the next part is to update the QB AI to take into account an accuracy rating. That should be pretty cool!
Passing accuracy update rolled out and model updates completed!
Regen3 Update #5: Model and Passing Accuracy - YouTube
I thought it through, and I think I have a good strategy for today's work:
1. Add the QB accuracy rating stuff in.
2. Add in the WR ability to adjust to the throw
3. Try to get the catching stuff added. (still not 100% sure of the best way to do this....)
We're getting down to the last few days of having lots of development time. (before I have to start the new job) Hopefully I can get a lot of progress done in the next 5 days. I'd like to have the engine to a place where it can at least run a complete play, from beginning to end.
That's it for the development progress backlog!
I'm working on some pretty cool stuff today having to do with QB pass accuracy. (and it lends itself pretty well to progress pics!)
The strategy for pass accuracy is to have a pass grid that has its size determined by the pass accuracy rating. (The grid is the list of all possible locations the pass could go, with the ideal pass position in the center)
I then randomly choose a node in the grid to set the target location for the pass. The idea is that a great QB will always have his passes land in a small square around his target, while a bad QB will have his passes land in a much larger square.
seems to make sense, right??
I have to iron out a few things with the WR adjustments, but then I'll post a video or pic.
Awesome, I can just keep coming here for updates.
This project looks really cool and it's awesome to see the progress!
One of my future "dream" games is something like this, but Blood Bowl themed. I keep downloading the Unity updates and reading about C#....maybe someday! :)
Do you use C#? Are you writing everything yourself or have you used any assets from other people?
I know what you mean about almost starting Unity. I had it on my computer for over a year before I finally buckled down and started learning it.
I'm using C# in my development. I started off trying to do everything myself, but I found that I'm just not that good at a lot of the things you need to make a legit game. Once I was at peace with that idea, I started buying assets from the Unity store for things I couldn't do myself. (mostly art and models, but I've also invested in some pathfinding)
Downloading assets for things I don't want to do, has really accelerated my development. In my previous games, I spent months developing decent a* system. This time, I downloaded one from the asset store and had it running within an hour....and it was light-years ahead of what I'd previously made. :(
The asset store has also allowed me to have more fun in the development. I'm not forced to do things I don't enjoy. So far that's been the best thing about working in Unity.
That's really interesting. It definitely seems like the asset strore is one of the biggest things going for unity. I've spent a little time looking at Unreal engine and it doesn't seem to have the same size of community.
Good for you for finding a "path of least resistance" when it comes to finding fun!
You definitely need to "drink the kool-aid" if you're going to do Unity. It's got some core concepts that are hard to get used to. Once you get the basic premise, it becomes pretty easy to bend it to your will.
Got a couple screenshots to show what I'm talking about when it comes to QB accuracy... Both passes are for the same WR route, and the ball is intended to go to about the same spot on the field.
High Accuracy Grid
Low Accuracy Grid
If you notice the black and the white bubbles, they are showing a couple interesting things. The black bubble is showing where the "ideal" pass target is. This has the WR speed, distance, running direction, and pass lob baked into it. The white bubble is where the pass is actually going to land. (ignore the other colors for now. they will become important later)
the idea is that the white bubble can be at any of the nodes. What I'm hoping is, by controlling how tightly packed the nodes are, I can simulate the ability of a QB to accurately hit his target.
Working on a video that shows it in real-time, with the ability of the WR to adjust.
New video uploaded Accuracy and WR Adjustments
In this video you'll still see the black and white bubbles from before, when the QB makes his decision to throw the ball. The black is the original "ideal" pass location and the white is the actual pass location, as determined by the QB accuracy. There are a whole bunch of low-accuracy passes in the video, so you can get a good feel for the randomness of a crappy QB. After that, there are 3 passes from a great QB to show what a difference accuracy makes.
The WR adjustment part is pretty simple right now. I have a simple internal timer on the WR that delays the adjustment a small amount from the time the ball is thrown. (opportunity for an awareness rating?) I started off with immediate adjustment, but quickly found that the WR could get to almost every errant pass, rendering poor accuracy fairly meaningless.
I think it accomplishes what I was hoping it would. It's at least enough for me to move on to the next thing. I was hoping to have a breakthrough idea about catching and holding the ball......but I didn't so that's going to have to wait. :(
I think I'm going to tighten up a couple things on the ratings, and then move on to run blocking and running AI.
This should make for some fun videos. :popcorn:
On another note, I just saw that the Axis Football developer is using Unity!
I'm getting a ton of great tips by watching his development videos and looking at the code.
Axis Football Dev Logs
It's like nerd catnip. :D
Thanks for the link and looking great on the accuracy!
So after watching a bunch of the development videos from Axis Football, I took some of the lessons and reorganized a lot of my AI and scripts.
Sooooooooo much better!
Unfortunately this took most of the last 2 days, so I won't reach my goal of having the engine do a full play. :( The good news is that I think I have about 75% of the run blocking AI figured out. I think I'm going to have run-blocking done in two phases.
1. Point of Attack (PoA): Think of this as everything that happen from the start of the play, to the line of scrimmage (LoS). When the play is loaded from the playbook to the active container, it does some calculations and figures out what the PoA is at the LoS. This is the point that is gong to be used to determine the priority list of who needs to get blocked, who should block them, and where they need to get pushed.
You might be thinking "Hey, can't you just design the blocking scheme in the play editor?" I thought that too. The problem I found is that O-Line assignments get broken too easily by strange defensive front alignments. I want the line to create its own assignments based on the intent of the play and the way the defense is lined up. Reason #2 is that you won't have to worry about blocking assignments as a player. You'll only need to worry about where you want to run the ball. The rest will be done for you.
2. 2nd Level / Open Field: This is everything that happens after you cross the LoS. This is where the play diagram is over, and it's all up to the RB. Blocking in this phase of the play is going to be pretty similar to blocking on kick returns. This is where linemen need to be able to look around, and pick out guys to block in the open field.
There are a few things I still need to work out when it comes to blocking. Since I'm not using "suction blocking", I need to find a way for blocker to stay on their guy. I also need to find a way to push defenders in specific directions, without resorting to pulling or suction. Right now, defenders will just slide off the linemen.
It took a while, but we're back in business. I apologize for the long break on this project, but there were a number of pretty boring reasons for the delay.
Other than those, I also came to the hard realization that I didn't yet have the skills I needed to accomplish what I wanted to in Regen3 project. It's not fun to know you're not skilled enough to make it happen, but at least I could do something about it. I took some time and worked intensely on my other project, to practice some new skills, and keep from getting discouraged on Regen3. (the other project being kind of like the sacrificial lamb of development projects)
Now that I look back on my old Regen3 code, it was time well spent. That stuff looks like it was written by a drunk monkey banging on a keyboard. We're going to do a complete overhaul on that mess.
Anyway, I'm back in full Regen3 development mode, and I think I've got a few things planned that are going to be fresh and different. (hopefully fun, too) Since my work blocks OS, I won't be able to update here as often as I do on my own dev blog. I'll try to provide updates here as often as possible, though.
Wow, last post was over 4 months ago!
Even though I've been locked out of FOFC by my day-job, I think I have a system figured out now.
I should be able to post more regularly again. ;)
On the development front, things have been progressing. I promise I didn't take the last 4 months off! (feel free to check my dev blog for details)
It's hard to get feedback from the blog, so I'll probably focus this topic on more of the technical 'behind the curtain' details and see if I can get some opinions from you all.
I have to finish up the AI/performance overhaul before we get back to the fun stuff, but then we're back to new feature mode.
(Picking up updates from the blog)
3 days into the new programming routine, and we're back to making progress!
The AI "structural overhaul" is complete. Remember how I was using framerate as a way to tell if we were missing AI cycles? Well.... that's not good. Framerate and AI shouldn't be tied together like that. If they are, that's probably a sign that something is wrong. Something was wrong.
My AI structure was set up like one huge tree. All players went into the AI tree and then branched off based on their role, position, actions, and the progress of the play. It makes a lot of logical sense, and it's fairly easy to troubleshoot, but it also means that there are a lot of unneeded calculations/checks done each cycle. Also, the program doesn't have the ability to skip sections if the CPU load is too heavy. I changed the structure so that instead of a single AI tree, I have 29 parallel "role-specific AI" running each cycle. Each specific AI also has the ability to bail out quickly if it doesn't apply.
Sounds good, right? Well..... it didn't fix the AI cycle skipping. Honestly, it probably made things worse. However, I'm running at a brisk 60-67 FPS at all times, including when I have 22 games running at the same time. That's pretty cool!
I was really hoping that I solved my AI cycle-skipping issues, but I didn't. Deep down I know I have some bad AI code, but my laziness was hoping it went away on it's own. :) I guess I'll still have to do the actual work of tightening it up.
On another note, I took an AI break and implemented the defensive play diagram system. I think it worked out pretty well. I might play with the colors and the icons, but it does the job right now. (Also made some small tweaks to the offensive diagrams, to sex it up a bit.)
Weird thing happened this weekend...
My goal for the weekend was to build a system of measuring how many (and which) AI cycles were getting skipped during peak load. My theory was that there are too many players calling too many AI when I have lots of games running at the same time. During those times, there are too many AI being called and some of them end up not getting run because the game loop finishes too quickly, and the remaining AI just get skipped. (that's how it's supposed to work) I have some evidence to support this theory, too. As I started adding in heavier AIs, I could see the players starting to skip cycles. I did some research and confirmed how it works, and made the adjustments to address it.
Which brings us to this weekend. All the adjustments are in, the cycle skipping problem didn't go away, so it's time to buckle down and really figure this out. This afternoon I got all the test stuff implemented and started investigating.
I generally run 2 games at the same time, as a default. With 2 games running for just under 3 seconds, I get 1200 AI calls. (evenly spread out across 26 AI) When I look at how I set the AI up, that's just about 100% of the AI getting called correctly. Cool. That's what I would have guessed.
Now when I jack it up to 22 games, I expect the AI counts to jump WAY up, (because 11x more players are calling AI) but to have a large spread between the individual AI counts. (because the low priority AI should get skipped when the game loop runs out of time) What I found was that all the AI calls are still exactly evenly split across all 26, and the overall total of AI calls went down.
That means by adding more players, I'm getting fewer total AI calls?
So I started running intermediate amounts of games to investigate. (4, 6, 10, 16, 20, 26, 30) As it turns out, the total AI calls drop fairly linearly after 4 games. OK, that's matching what I see, but it makes no sense... Unless I've been looking at the problem the wrong way all along.
What if my crappy AI isn't the problem? What if it was just a coincidence that the cycle skipping started when I implemented my heaviest AI?
So I still don't know what the problem is, but here are my top theories to investigate over this week.
I think I know how to quickly test each of these, so hopefully there will be a direction identified by the end of the week.
I ran a few experiments this morning to test out the theory that bad AI is causing the cycle skipping issue. Here’s the data:
I started off with turning all the AI off, with the exception of the ones relevant to kick returning. Then I ran the kick return and measured the number of cycles for each amount of games. (Kick return is the hardest on the AI system because of all the open field calculations)
The numbers looked about as I expected. With the AI set to run 0.05s, it became unplayable at 14 games. At 0.1s it became unplayable at 12 games. At 0.2s it was unplayable at 8 games. (and pretty bad for 4 games)
No smoking gun there, so I turned on all the AI to set a baseline to compare the numbers to. That’s when it got interesting. After turning on all the AI (the ones not being used) and setting them all to refresh at 0.15s, the game became unplayable at 6 games and quickly went out of control.
That’s a clue!
Nothing changed with the animation or physics, but I could send the game into the crapper just by turning on and off AI. All of those extra AI are built with a quick ‘bail-out’ at the front end, so they only do 1 simple flag-check calculation before exiting. That shouldn’t cause this problem. However, actually calling the various AI might be a bit more process intensive than I’d thought. Just the simple fact that they need to be tracked and called might be killing my cycle time.
I’m going to do more investigation into this, and see if I can come up with another experiment for tomorrow morning.
I’m pretty sure I’m almost at the bottom of the AI cycle skipping problem. I ran a couple more experiments to confirm what I was thinking yesterday, and everything is pointing to the AI code/processing as the culprit.
First, here’s the updated graph:
I ran a quick experiment to see if the physics collisions are causing the problem. Basically, I turned off the field for all the games and played them without all the player-to-ground collisions. If collisions are a problem, then this reduction should show up in the data at least a little. If you look at the line, it falls almost exactly on top of the line with the field on. That seems to suggest that lowering the collisions didn’t affect the problem one bit.
Cool, let’s look at the AI a little more. I reran the first experiment (turning off everything but kick returns) and tried it with pass plays. My theory here is that if the data lines up exactly on top of the kick return AI, then the AI code itself has little/nothing to do with the problem.
That’s not what happened, though. The pass play data came out almost as bad as the all-AI data. Yikes! In fact, the game became unplayable at 8 games.
I’m pretty sure I figured it out. The problem is a combination of poorly written individual AI, and the way the individual AI’s are being processed.
I had a feeling this was the case, but the roadblock for me was the incorrect interpretation of how the AI was supposed to be processed. Everything I’d read about my AI package told me that the lower priority AI would just be skipped if I ran out of cycle time. That’s not what happens. What actually happens is all of the AI gets evaluated every cycle. If we run out of cycle time, the next player in line to be updated gets skipped entirely.
I’d always relied on this to cover up for the brute-force approach I’ve been using to write the code. I have a lot of evaluation code (like collision avoidance) that cycles through all the players on the field for the awareness-type evaluations. Sometimes I cycle through all the players more than once in the same script. (open field blocking)
Even though I’m only cycling through 8 or 15 players at a time, you can see how this has the potential to eat up a lot of cycle time when it’s done for every player, every cycle. That’s just bad design. (I knew it was bad at the time, but I wanted the damn thing to work!)
Luckily I’ve learned a lot since most of this was written, and I have new techniques in my toolbox to replace the brute-force methods I used before. That shouldn’t be hard to fix, since I’d planned to scrub/refine all the AI anyway.
The other problem is how the AI is processed. The ‘all or nothing’ processing kind of threw me for a loop. From the earlier testing, you can see there’s a difference when we turn off unused AI. That definitely feels like the right way to control AI processing. Unfortunately, the AI package I’m using doesn’t allow for that.
So I guess I have to write my own controller!
So I spent some time yesterday and this morning whipping up an “AI Master” to make determinations about which AI are needed, and which aren’t. It only allows processing on the needed AI, and blocks off all others. Ran a few tests and I’m replicating the cycle times for when I turned off all the unused AI’s. That means it works!
So we’re back on track!
It kind of sucks to lose 3 days to this investigation, but I’m glad that I understand how all this works. I also know a lot more about my ‘AI refresh rate’.
Anyway, back to the fun stuff!
Major AI Refresh #1: Passing Plays
Passing plays were the first thing I wrote, so they are in the worst shape right now. I've been running plays over and over again, to bathe in the embarrassment.
Here's the hit-list of what needs to be updated:
All - Need to implement more position specific ratings. Need to make the code more efficient.
OL - Not horrible right now, but they need better recognition of late rushers and overloads.
DL - Everyone is an 'agility rusher'. I need to allow for bull and speed rushing.
Route running - Need some collision avoidance. The worst thing right now is when 2 WR run into each other and get stuck.
QB - Needs a LOT of work. Reads are terrible right now and the QB never reads past the 2nd option. QB often throws into coverage. Accuracy is off on crossing routes. Throw arc is generally too low, and needs to be variable. Need to implement in-pocket movement and scrambling.
Man Coverage - Not terrible, but need to tighten up the collision avoidance and positioning.
Zone Coverage - This is a mess from start to finish. Complete overhaul.
Blitzing - Actually, this might be the best one I have right now. Not much needs to be done.
Blitz Pickup - Too 'predictive'. Need to make the RB read/react more than predicting the blitz.
Screen Blocking - Still a mess. Complete overhaul.
Misc - Need to implement incomplete pass resolution, interceptions, tackle animation, missed tackles animation, throwing animation
Here we go!
I'm actually pretty pumped about this morning's progress. Today I started implementing the positional ratings in the AI, and it's working out pretty well.
In case anyone forgot, my ratings strategy has been to keep things as simple as possible. (I had a blog post about speed, but I think I lost it)
does a quick search to find reference blog posts..... can't find them..... ugh!
Since it looks like I lost some of my old pre-GoldenCrest blog entries, I'll recap:
I want to keep ratings as simple as possible.
Seriously, the strategy is to try and hold the number of ratings as low as possible. I want to have enough to have a variety of players, but not so many that individual ratings become nearly meaningless. The focus of the game is on building and managing your team, not to see who can come up with the optimal interpretation of the ratings system.
With that in mind, here's where I'm currently at on ratings:
Speed - The only rating I use for running. I have all players with the same acceleration and all players have nearly perfect agility. It sounds weird, and it looks a little weird at first, but it really works for differentiating players.
Strength - This is only used for pushing. The stronger the player, the more he can push other players. Just that simple. It actually makes a huge difference in the engine, because it comes in to play every time players collide.
Positional Skill - I combine every mental rating into a single positional skill rating. Each player is rated on every position. Your top WR might have a 95 for his WR Skill, but he's rated below 30 in every defensive position. (remember, players play both ways) How much do you really want to invest in a 1-way player? Do you put him on the field for defense and hope he doesn't get exposed? Do you use up additional valuable roster spots to cover for him? Those are the kinds of decisions the game is about.
Back to today's progress. I started implementing positional skill in the AI. The first thing added was a "decision making timer". This is to simulate the idea that a player who is bad at a position takes longer to make decisions.
I like to imagine this as a kick returner. A great KR will bob and weave his way through the holes until he finds daylight, then he turns on the afterburners. The difference between a great KR and a bad KR is that bobbing and weaving. By using positional skill and decision making timers, I can now see the difference between KRs on the field. The 100 rated KR bobs and weaves through tight traffic. The 20 rated KR makes a cut and runs as hard as he can into the pile. The 60 rated KR will sometimes make a cut or two if there's a hole, but can't navigate tight traffic.
Man, this is getting more fun every day!
I forgot I'd copied all the pre-Website blog stuff here!
This morning is all about collision avoidance.
As I was looking through the man coverage AI, I remembered that there was some fairly brute-force methods to keep players from running into each other. Early on, that was a huge problem. My solutions evolved over time, but they got more and more complicated each iteration.
The last version I implemented did something like this:
All of that takes about 25-30 calculations, done for every other player on the field, for each defender in man coverage, every AI cycle. (30 * 15 * 6 * 10 times per second) = 27,000 calculations per second for each game. Just in man coverage collision avoidance!
I've been using sphere casting to check for running lanes, and I think it should work nicely for collision avoidance, too.
Basically it works like this:
#2 and #3 are the same statement, so we've gone from 25-30 to 1 statement. The potential savings are HUGE.
That's the downside of learning this stuff as you go. I make a lot of dumb mistakes, because I don't know what this thing can do.
Know your tools.
I got some "bonus time" today and tried to make the most of it by reworking the QB AI. As with most of the passing AI, it was done really early in my learning process, so it needed a pretty big overhaul.
Remember way back when I started using sphere casting instead of cycling through all the players? Well, it looks like there's a similar opportunity in the QB 'is the receiver open?' checks. Each time the QB looks to see if the WR is open, he cycled through everyone on the field and checked distance to the optimal throwing spot. Once again, way too many calculations.
I ended up replacing all of that with this concept:
Make a sphere at the throw spot. If anyone overlaps with the sphere, call the WR covered.
By playing with the radius of the sphere, I can fine tune what we call "open". (seems to work great, too!)
I also implemented a new scrambling system. Each player will have a "Scramble Tendency". This is going to be directly tied to the length of time he'll stay in the pocket before scrambling. Right now, I have the 100 scramblers hang in long enough to read 1 WR. (maybe 2 depending on the play) The 0 rated scrambler will probably hang in the pocket for 4-5 seconds. More than enough time to get through all reads. Once the QB breaks the pocket, he then moves into the open-field running system.
I've almost got the QB accuracy implemented, too. This is going to be a direct result of the QB Skill rating. For the QBs, the skill rating affects accuracy instead of decision making time because it just seems to play out better that way. The decision timer for QBs was more frustrating than anything, and it was hard anticipate the exact effect. (was a sack the result of coverage, or the decision timer? ugh...)
So far, the new system testing looks good. I'll get a video up as soon, I hope.
Ventured into the man coverage AI this morning, and I think we can call it done!
Most of it was tuning performance and making sure the coverage "shading" looked right. I was having a hell of a time with LBs covering RBs on swing routes. The LB was diving into the line and getting stuck because he was always trying to gain tight coverage while the RB was running across the backfield. Now players just sort of follow across the field, maintaining their depth, until it looks like the RB is going to actually head upfield. At that point, the coverage gets tighter.
Not perfect, but it looks a LOT better.
I also had a tough time with how to implement positional skill. I started off with the decision timer, but it looked wrong. (just like in the QB AI) I'm starting to think the decision timer only works with running.....
Anyway, after testing a bunch of options, I settled on a system for coverage. Basically, positional skill affects these things:
Position when covering someone. Bad players will have wildly varying coverage position. They might be tight one play, and then play 5 yards off another play, and then completely botch the coverage on the next one.
Interception Ability: Basically catch percentage
Pass Breakup Ability: Bad players allow more catches even when in coverage.
I purposely left speed and strength untouched from skill adjustments. It just seems more interesting when your CB is fast, but is often out of position. I lowered the QB skill to see what would happen, and sometimes the fast CB could make up for bad coverage to get to poorly thrown passes.
Which bring us to the last item implemented this weekend, interceptions.
I have no idea why it took me this long to have interceptions, but it's in there now. It really changes the game! Remember that fast CB who sometimes gets to poor passes? It's really cool to see him pick the pass off and try to house it. idk, there's just something about it that's oddly satisfying.
Next up, Zone Defense!
Zone defense is done, but I'm still not 100% sure about it. Everything has been updated for code efficiency and logical sense, but I can't quite tell if it's exactly right. I still have a few problems in the player movement engine that need to be addressed, and I think those problems are popping up in how the players execute the zone defense assignments. Specifically, I don't have a great solution for players who are really close to their objective. (no slowing down or stopping has been implemented) This shows up in zone defense more than anything because guys are sometimes sitting in their zones, waiting for someone to come through. It's hard to tell if the player freak-out is the movement engine, or if there's an actual AI glitch.
Aside from that issue, it seems like it's working. Everyone is in the right spots, they just do a freak-out dance more often than I'd like. I don't anticipate there are going to be very many all-zone plays, but I have one in my test playbook and it seems to work reasonably well. (considering how big the field is relative to the player size) The safeties sure seem to be ball-hawking any errant passes. That's pretty cool to watch.
Positional skill for zone defense was designed to affect zone position and size. It seemed to make the most sense for bad defenders to have problems with exactly where the zone is supposed to be, and how large it is. Once again I tried using decision making time, but it just looked wrong.
I think that's only going to work for running the ball. So far, that seems to be the best use of it.
Before I tackle the next AI, I think I need to fix up that player movement engine. It's a little frustrating to still see those freak-outs. I should be past that by now.
Good progress day today. I knocked out a few things that have been bugging me lately. Here's the hit-list:
I also started digging into the QB "is the WR open" scripts. I'm not 100% satisfied with the QB ability to read openness yet. They always can find the wide open WRs, but I'm having trouble with 100 rated QBs throwing into weird coverage situations and missing open guys in the middle of the field. What I'm realizing is that it's way harder to define what "open" means than you'd think. Right now I'm using a system where I make a 4 yard 'bubble' around where I think the pass is going to land, and see if there are any defenders in it. I also make a 2 yard bubble around the WR and see if he's being tightly covered. The result of the two bubbles is the openness.
What I'm finding is that the QB is not throwing to guys I'd consider open. The most obvious one is a guy who is standing still, with a defender behind him. If the QB has a clear pass to the WR, he won't take it because of the defender standing 3 yards behind him. On the other hand, I'll get QBs making ill advised slant route passes that get picked off by roaming safeties. I can't increase the leading bubble because then nobody is open. I can't decrease it because then I get more throwing into coverage.
I probably need to rethink the whole system.......
Just wanted to chime in and say how cool it is to read these updates...definitely way beyond my skill level - so impressed by your ability to troubleshoot and diagnose!
Thanks, I appreciate that! It's been a lot more fun to troubleshoot lately. Having a game engine that actually resembles football makes it more interesting and satisfying to dig into the details.
I also can't say enough about Unity as a tool. It allows me to do things I never thought I'd be able to do.
It’s a little frustrating to spend a whole day’s worth of keyboard time on a single issue, but I really needed to get this QB read AI fixed. That being said... I GOT IT!
It was getting really frustrating to see the QB consistently pass up good throwing opportunities. I tried a lot of things to get it to work, but the QB was always reading open receivers as covered. I created some troubleshooting visuals to confirm what he’s looking at, and got even more confused.
Here’s what the QB is seeing on the slant route. The big yellow circle is reading the space in front of the WR and the orange is reading the tightness of the coverage. The little yellow circle is where the pass is intended to go.
This should be an open receiver, and the QB should throw the ball. (the CB looks close to the orange circle, but he’s not actually overlapping. Also, the yellow circle is returning an overlap with the CB…?) There are no options in the code to allow the QB to pass up open throws, so it really should happen.
It never happens, though.
It’s so frustrating because this isn’t rocket science. Does the defensive player’s circle overlap with either of those colored circles? If yes -> covered. If no -> not covered. Seriously!
After a LOT of investigation around 4 lines of code, I figured it out.
OverlapShpere is defined by its radius. Spheres are defined by their diameter.
Here’s what the QB is actually seeing
omg I’m dumb…….
Well, it’s fixed now. What I see is the same thing the QB sees. He can reliably hit that slant now.
I need a drink.
It looks like that QB thing was the last major hurdle to finishing off the base game engine. After figuring that out, I was able to hammer through a lot of the remaining tasks. Basically, I cleaned up all the remaining AI's and implemented positional skill in almost everything.
What I have left is:
That's it! After those are knocked out, I can move on to turning it into an actual game!
The only thing on the list that's going to take some thought is the punt target selection. I made a design decision early on to eliminate fieldgoals, so punting needs to pick up the slack. I really want punts to be more of an offensive play than it is in the NFL. I'm toying with the idea of not allowing touchbacks, which would turn punting into a potential scoring option. (essentially replacing the fieldgoal)
Seems like it will be cool....on paper. I hope it works out.
I think I'll knock these items out pretty quickly. After that, we're going to move on to depth charts and playbooks. (and stringing multiple plays together to make a full game)
Well, I think the AI and ratings implementation is basically done! It's been a long haul to get past the base game engine, and I put off a lot of other features to get this right, but I think it's going to be worth it. I'm pretty sure I've locked in the ratings, which was the most important thing. I wanted to be sure which ratings were important so the game/interface could be designed around those, and not have to go back up redo interface as I update the game engine.
There are going to be changes in how the ratings are used as we go through more detailed testing, but I think the ratings themselves are mostly locked down. Here's how we ended up, and what's going to be reserved for future refinement:
Positional Skill Ratings
Non-Core Ratings (probably invisible to the player)
The interface strategy is to keep ratings as simple as possible, so the focus stays on roster and depth chart management. When evaluating players, I want it to be easy to determine who is better, without you having to spend a lot of time figuring out "player style" or "fit".
The decision making process should be focused entirely around getting the best players you can, with your limited finances and roster spots.
There are still some game engine things that need refinement. (like fine-tuning positional rating effects on performance) I'm going to hold off on doing any more of that until the game has progressed enough that I can manage rosters and really get into the details of following players through multiple games. That should give me a good feel for what works, and what doesn't.
Anyway, on to the next phase of the project! WOOHOO!
Honestly, as much fun as the AI and game engine was to work on (for the last 12 months) I’m glad to be able to move on to something new. The next part is going to be really interface heavy, and I’m not very good at that. It’s going to be rough for a while….
First up is the depth chart interface. I started thinking about this way back in March (link here: )
After reviewing where I was then, I don’t think a lot has changed. I want all the work it takes to prepare for each week’s games to be as simple as possible, so you can focus on roster management. However, your per-game decisions have to be meaningful or roster management is pointless. I also don’t want to bury the player in spreadsheet type screens that don’t add much to the experience.
My first attempt at designing this:
Once you see it on the “screen”, there are some obvious problems. First, there are WAY too many text fields. It’s starting to look like a spreadsheet. Second, it’s going to take up a ton of screen space. I really want to see the players practicing while I manage the depth chart. Third, it requires drag+drop to make it work. Eventually I want to create a PS and Xbox version of this game, so drag+drop isn’t going to work real well. (besides the fact that drag+drop is something I hate in games)
Ok, let’s try something else:
whoah, now we’re getting somewhere! The practice field is visible and the screen is nicely laid out. Looks great!
…… then I tried to implement it. As it turns out, it’s way easier to make things fit on the whiteboard than it is when you actually create them on the screen. The number of fields that this system requires makes it impossible to fit on the screen, and still leave room for the practice field. You end up with the spreadsheet look again. I also ran into a new problem I wasn’t expecting. As you adjust the resolution or screen size, the precise layout starts to become ‘unstable’ and the roster column can run into the depth chart rows. Yikes!
*begin random flashback*
There was a time, way back in the day, when I wanted to be an author. I had a lot of stories in my head, and wanted to get them out into books. I had a friend (Lee) who was already an author, and loved to talk about the craft. One day, as we were talking through one of my stories, he made a comment that always stuck with me.
“Sometimes, you need to kill off your favorite character.”
I was trying to get through a story where my favorite (and main) character was in a situation where he ‘should’ get killed. I was trying to find a way to get him to survive. Lee was pointing out that the story is what’s really important. The characters were living in the story, and it needs to go where it goes. I shouldn’t get too attached to specific characters if it hurts the story.
*end random flashback*
So I’ve been trying to find a way to have the practice field be the center of attention, but maybe I need to let that character die. If forcing that hurts the overall experience, it has to go.
So I went back and redesigned the depth chart interface with ruthless efficiency of space in mind. Strip it down to the bare minimum of space needed, and use the center of the screen if necessary. I need to be able to manage Offense, Defense, and Special Teams depth charts in a single screen, with the smallest amount of real estate possible.
Here’s what I came up with:
The concept is that you have a roster of 12 players, so offense , defense, and special teams each have 12 predefined depth chart positions. (the playbooks will reflect this) Every player on your roster will fit into one of these slots, for each of these packages. (remember, players play on both sides of the ball)
Since we’re managing all the depth charts and packages on one screen, you need fast access to the ratings. If we put them on each player record, it starts to make sense to put it in a spreadsheet format.
But we hate spreadsheets…….so let’s make it an overlay on the practice field.
But if we do that, we can’t see the ratings because the players on the field are too distracting.
So let’s use bright colors to enhance the rating boxes. And if we’re coloring the rating boxes, let’s scale the color to match the number! Now we have multiple levels of information in a small package.
I think it works pretty well. The other added benefit is that it’s relatively small on the screen. I can adjust resolution and screen size way up/down and nothing becomes unstable or falls out of place.
Now I just need to make it functional. 😉
interface stuff is hard.
It was a lot harder to do than I though it would be, but I’m pretty sure I have the depth chart screen functional.
Here’s how it works…..
Starting from the left, the Offense, Defense, and Special Team “Squads” are listed vertically. Right now, the offensive squad is selected and highlighted. The brighter purple positions are what we’re considering the “starters”. Those are the guys that are in almost every play. The darker purple are backups or guys who come in on fewer plays. (I still might make WR3 a starter. Depends on how the playbooks shape up)
The defensive and ST squads are not highlighted, but you can still see what position the players are listed in. For example, Brent Favre is QB1 on offense, LB3 on defense, and ST3 on special teams.
I’m using nicknames for players, so the names section of the depth chart screen takes that into account. It figures out what combination of first, last, and nicknames fit in the field and displays it appropriately. Terrell Owens doesn’t have his nickname (“Love Yourself”) shown because it won’t fit. Favre has his first name dropped to fit “Wranglers” in. The random players all have the full first-nick-last names shown. etc…
Speed and Strength are just showing the raw values because I haven’t scaled that in the engine yet.
For the rest of the positional ratings, they are colored with the values. It simply shifts linearly from green to red on the RGB chart, based on the 1-100 ratings scale.
By clicking on Osweiler, he gets highlighted and designated as the ‘active’ player to manage. If I wanted to change his offensive position right now to QB1, I’d click on the QB1 square and he’d flipflop with Favre. I started messing with a system where you click an “up/down” button to shift the players up or down in the depth chart, but it became a pain to use. This just seems to be more direct and simple way of changing positions, even if it has it’s own flaws. (you want to do your swapping starting from best players to worst players, or you end up doing extra swaps)
As I was messing around with this in-game, it got hard to follow. I found that I was usually trying to set a single player at a time, so I need that player to stay highlighted when I switch between squads. (previously it just kept the row highlighted) That’s a lot harder than it sounds.
Anyway, I got it working with a little brute-force, and I think it’s fairly effective. Here’s the string of screens for managing Osweiler.
As I starting using this more in-game, I’m sure I’ll play with the colors and formatting. For right now I think it’s effective enough to continue to move on.
Honestly, I’m not sure what to work on next. I think a playcalling screen seems appropriate, but I also need to start transforming the engine from single-play resolution to full-game resolution. Maybe I’ll go chop some wood or something, and see if I get inspired.
Not really a major progress update for this post, so much as a record of lessons learned....
*begin random flashback*
So back in the day when I was a complete hack, I used to try and impress my friend Lee. When he wanted to see how I got something to work, I tried to be really clever with the code and how I managed my data. I'd work really hard to get a block of 100 lines down to fewer than 10. Most of the time, I could figure out a really 'elegant' way to squeezing every bit of code efficiency out of it.
Then I'd ship it over to Lee and he'd be all "It's really efficient, but there's no way you're going to be able to fix this if it doesn't work."
Me: "What do you mean? I got 100 lines of code down to 10!"
Lee: "Yeah, but now it's so efficient that it isn't obvious what it's doing. Your code is doing multiple things at the same time, and your variable are generic and getting reused for different things. In 3 months you'll have no idea what this does."
Me: "You just hate me."
*end random flashback*
So I was playing around with my depth chart, just looking for the next thing to work on, when I noticed that sometimes a player would get cloned when I changed his position.
.. uh oh.
It didn't happen every time, but it was happening enough that I had to fix it right away. So I jumped into my code and tried to spot the error in my logic. Of course, I'm impressed with how I wrote the code and how efficient it is. However, it's only been a couple days since I wrote it, and I already can't figure out exactly what it's doing.
Damn you, Lee.
I hate it when he's right, but he's right. I ended up rewriting most of it to be really straight-forward and clear. It took 10x the number of lines, but it's extremely clear how it works. Then I started from the top and worked my way through it until it became obvious what the problem was. I was using a "If-then" type structure instead of a "switch" when checking position during a depth chart swap. What happens is that the player could get his position swapped, but then get his new position checked again and get swapped a second time, with himself, and get cloned. (I know, it's confusing)
Anyway, I fixed all that and started being really conscious about how 'clever' I make my code. It's already paying off, too! I just finished up the part where I reconcile incoming plays with the depth charts. I had another really confusing error, but it was easy to solve because the code was laid out in a simple and straight-forward manner.
I hate it when Lee's right.
For those who have been following along, what new important detail is shown in this screenshot?
Did you get it?
The defensive team is on offense! (orange)
*end attempt to make the mundane seem exciting*
That detail doesn’t seem too important until you put it in context of the game engine development so far. Up until this screen, I always had a predetermined offensive team and defensive team. This is the first time the game situation decides who is on offense or defense. What you see here is the first play after an offensive touchdown.
oh yes, we’re scoring touchdowns now. 🙂
We’re using all kinds of things that weren’t ever part of the engine before!
It’s pretty awesome to actually have drives. For a long while, I had a hard time believing I’d ever get here. Just before I put it away for this post, I had an 11 play drive that ended up stalling out near the 5!
That was a sweet moment. 🙂
There’s a lot of tuning and bug squashing to be done, that’s for sure. My strategy is to get to a functional game engine running so I can start a “dynasty” with a single team, and see what we see. I’ve found it’s a lot easier to pay attention to the details when your players live on longer than 1 play. I’m hoping that I can follow my team through multiple “games”, and be more effective in refining the engine along the way.
Who knows, maybe it will all flame out and I’ll have to do more work before a dynasty works. We’ll give it a try and see what happens.
To make it interesting, and to test out multiple parts of the AI/engine, I’m going to create a league and implement some house rules. I have some ideas right now but I want to think this through to make it both interesting, and effective.
More to come……
Kicking off Exhibition Season #1!
Here’s the dynasty plan:
The “Reds” and “Purples”
The “Greens” and “Pinks”
The “Blues” and “Yellows”
I think that’s it. Basically I’m going to draft teams, and play out a full exhibition season to see what happens. I’ll also keep track of the game engine observations as I go. Right now, I think I’ll fix/update anything that can be done in under 3 hours, prior to the next game being played. (3 hours is about 2 weekdays worth of keyboard time) Anything that will require more than that, I’ll keep on the list until after Exhibition Season 1.
Now to generate the player pool!
i'm following along by the way, good stuff, and very exciting progress in the past couple days!!
So I got the Exhibition Season 1 allocation draft pool created in Excel, and was pretty happy with how the talent was spread out, when something occurred to me....
How am I going to get these players into the game?
I can't believe I didn't think of that problem earlier....
I've got 2 options. Option 1 is to manually edit every player (72 in all) to transfer the spreadsheet data to the engine. That would take FOREVER! The other option is to recreate the draft pool generation right in the game. That seems like a better idea, but the I either have to create all the draft interface stuff in the game or still find a way to to it in excel.
Long story short, I decided to just create the pool in the game and write an exporter to put the data into excel for the actual draft. I really don't want to spend the next week(s) designing a drafting interface, when the game engine needs so much work.
Anyway, I'm plugging away at it and should be ready to kick off the draft in a couple days. (maybe tomorrow if everything goes according to plan!)
|All times are GMT -5. The time now is 10:15 PM.|
Powered by vBulletin Version 3.6.0
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.