I introduced my velocity code in an earlier tutorial, so this one won't be entirely new. But since I think my velocity system is one of the best things I've written, I thought I'd go into more detail on it.
As you know, id software's walkmove() and movetogoal() statements, which I call code-based movement, are rather slow. So slow, in fact, that they make the Tutor Bot look rather dumb at first, even though he isn't.
You may also know that more so-called advanced bots, like the Reaper and FrogBot, use velocity movement. This is quite difficult to manage, and the results are not always preferable. I personally think the Frogbot looks drunk with his sideways walking. Another thing, velocity movement does not navigate for itself like code-based movement would.
I am more interested in what the bot can play or do, as opposed to how much like a player he moves. I didn't want to spend three months on making him slide down a ramp. I wanted easy navigation and fast velocity. So, I combined them.
In other words, I will use walkmove() to navigate and then give the bot a little "push" with velocity movement. This takes four lines of code. It looks as smooth as any other bot. And it's adjustable to any speed. How you like me now?
Let's take a look at this and see if I'm really as good as I want you to believe. Open up tutor.qc and scroll down to the routine bot_walk() where you'll see this code:
if (self.goalentity != world) movetogoal(20); else coffee_move();Here, if the bot has a goal item, he will use movetogoal() to navigate toward that item. If not, he'll use my wall-hugging roam routine. Pretty simple, but pretty easy. The problem is that movetogoal() is slow. Thus we want to add our new speed code. Change that stuff to this:
if (self.goalentity != world) { movetogoal(20); makevectors(self.angles); self.flags = self.flags - (self.flags & FL_ONGROUND); self.velocity = v_forward * self.speed; } else coffee_move();Boo-yah. That's the new velocity code. You see, he still uses movetogoal() to get to his object. But right after that statement, we figure out what direction he's facing, and give him a little push. The rate of the push is stored in self.speed, which we will set later.
// ****************************** void() coffee_move = // ****************************** { // this is the best subroutine i've ever written, and probably the // most powerful bot roaming function. i hope you credit me if you use // it. basically he strafes along a wall, then turns at a 45 or -45 // degree angle at the wall's corner. i have seen my bots do laps // around entire levels with these three lines of code if (walkmove (self.angles_y, 20) == TRUE) { makevectors(self.angles); self.flags = self.flags - (self.flags & FL_ONGROUND); self.velocity = v_forward * self.speed; return; } if (walkmove (self.angles_y + self.button1, 20) == TRUE) { if (self.button1 == -90) { makevectors(self.angles); self.flags = self.flags - (self.flags & FL_ONGROUND); self.velocity = v_right * self.speed; } else { makevectors(self.angles); self.flags = self.flags - (self.flags & FL_ONGROUND); self.velocity = v_right * self.speed * -1; } return; } self.angles_y = self.angles_y + (self.button1 / 2); // every so often, he'll change his wall-hugging direction if (random() <= 0.02) if (self.button1 == 90) self.button1 = -90; else self.button1 = 90; };Alright, don't be afraid of the length. Conceptually, think is that same routine. Here's a summary: If he can go forward, we push him forward with v_forward. If he is going right, we push him right with v_right. If he is going left, we push him left with ... wait, there is no v_left in QuakeC. But what is the opposite of right? Yup, negative right. So we push him forward with v_right time negative one.
// ---------------------- void() bot_strafe = // ---------------------- { // this routine is called every frame during combat, // so he strafes and dodges even while shooting bot_check_ammo(); if (!visible(self.enemy)) { movetogoal(20); return; }There's a movetogoal(), so we want to add our speed code. Make that last bit look like this:
if (!visible(self.enemy)) { movetogoal(20); makevectors(self.angles); self.flags = self.flags - (self.flags & FL_ONGROUND); self.velocity = v_forward * self.speed; return; }Drop down a little and you'll see this:
// chasing the player here else if (self.weapon == IT_SUPER_SHOTGUN) movetogoal(20);We will change that to this:
// chasing the player here else if (self.weapon == IT_SUPER_SHOTGUN) { movetogoal(20); makevectors(self.angles); self.flags = self.flags - (self.flags & FL_ONGROUND); self.velocity = v_forward * self.speed; }Good, we're done with bot_strafe(). We want to keep our speed code inside brackets so it's seperate from anything else he may do. As I said earlier, you're bot_strafe() routine may look very different. Just remember that anywhere you see a walkmove() or movetogoal(), you want to add those three new lines of code, and try to keep it all in brackets.
// -------------------------------- void() bot_run_slide = // -------------------------------- { local float ofs; // this is a cool strafing routine if (self.lefty) ofs = 90; else ofs = -90; if (walkmove (self.angles_y + ofs, 20)) { if (ofs == -90) { makevectors(self.angles); self.flags = self.flags - (self.flags & FL_ONGROUND); self.velocity = v_right * self.speed; } else { makevectors(self.angles); self.flags = self.flags - (self.flags & FL_ONGROUND); self.velocity = v_right * self.speed * -1; } return; } self.lefty = 1 - self.lefty; walkmove (self.angles_y - ofs, 20); if (ofs == -90) { makevectors(self.angles); self.flags = self.flags - (self.flags & FL_ONGROUND); self.velocity = v_right * self.speed; } else { makevectors(self.angles); self.flags = self.flags - (self.flags & FL_ONGROUND); self.velocity = v_right * self.speed * -1; } };Yes, like the new coffee_move(), this is a lot longer. But like that new routine, this operates the same. If he is strafing left, we push him left; if he's strafing right, we push him right. End of story.
bot.speed = 200;Boom, your shiny new bot speed system is ready to run. If you're one of those fair-minded guys, you can do this:
bot.speed = cvar("sv_maxspeed");The bot will move exactly as fast as the player If the user prefers to turn up his speed, the bot will move quicker too. If you like to have fun, try this:
bot.speed = floor((random() * 6) + 1) * 100;Hehe, cool. This creates a random number from one to six and multiplies it by 100. Thus, a random bot speed from, 100 to 600 will be created, meaning that each bot will run and strafe at a different rate. You could incorporate this into a difficulty system. I like it.