Aumentar la posición del sprite incrementalmente muy rápido sin demora - Python

I'm making a PONG game for a school project using Kivy in Python. So far thanks to this forum I've made myself some AI for the NPC paddle. This is the code:

    if self.ball.y < self.player2.center_y:
        self.player2.center_y = self.player2.center_y - 4
    if self.ball.y > self.player2.center_y:
        self.player2.center_y = self.player2.center_y + 4

This is in a method of PongGame() class called ArtificialIntelligence().

I use this to call it:

Clock.schedule_interval(game.ArtificialIntelligence, 1/300)     

This allows me to call it once every 1/300th of a second. However, anything more than 1/300, I seem to have no difference. I.e. 1/9001 does not call it once every 1/9001th of a second.

The way it works is that it increases the y coordinate 4 pixels relative to the balls position, and it does this once every 1/300th of a second, hence why it doesn't "lag" at this rate. This is basically an "easy" mode for the player. If I want to do a "hard" mode, I need to make the NPC more accurate. I can do this by doing

self.player2.center_y = self.player2.center_y + 20

Something like this. This would be extremely accurate. HOWEVER, it does NOT look "fluid", it looks "laggy". I assume I could get the same amount of movement by calling the method more often instead of changing the amount it moves via altering the pixel movement. However, I don't know how to do this, because, as I said, changing it from anywhere above 1/300 seems to make no difference.

This is how I use my paddle:

if touch.x < self.width/3:
    self.player1.center_y = touch.y

and I can move it as fast as I want because this updates as I move the mouse. And it looks fluid because it updates as often as it needs to update. I don't know how to do this with my AI.

Does anyone know how I could basically make the NPC paddle more accurate, allowing me to do Easy-Normal-Hard, etc, while retaining fluidity and no lag? I see only one way I could do it: Increase the amount the method is called.

However I bet there is a better way and I don't know how to do it. Does anyone have any Idea how I could do this? Thanks.

Edit: it looks like I can do it like this:

Clock.schedule_interval(game.ArtificialIntelligence, 1/300)     
Clock.schedule_interval(game.ArtificialIntelligence, 1/300) 
Clock.schedule_interval(game.ArtificialIntelligence, 1/300) 
Clock.schedule_interval(game.ArtificialIntelligence, 1/300)         
Clock.schedule_interval(game.ArtificialIntelligence, 1/300) 

But that seems REALLY ugly and REALLY inefficient... I'd MUCH prefer a cleaner way.

preguntado el 30 de julio de 12 a las 10:07

3 Respuestas

At 300 frames per second, the problem is not in the rate of updates because you are exceeding the human eye's capacity to perceive movement by a factor of 50 or more.

The jerky movement comes because the ball is following a linear trajectory while your paddle is just hopping to where the ball is now. Ideally, your computer player could compute where the ball will be when it hits the plane of the computer paddle and then take a very smooth course to that location at 30 frames per second or less. Sure, the prediction math requires a tiny amount of trigonometry but it is the "right" way to do it in the sense of that is how a good player would play, by anticipating.

It would be far easier to just increase the size of the computer's paddle which would also give a visual indication to the human player of just how much harder the game is. When the computer's paddle has become a wall, the player would see that there is no winning to be done. The larger paddle would have the side effect of being less jerky, but whether this is a "good" way is your decision.

Respondido 30 Jul 12, 12:07

I actually had an idea of using trig in order to calculate where the ball is going to land, kind of like this: Where the hypotenuse of both triangles will be the trajectory of the ball. I'm pretty terrible at math to be honest and I have absolutely NO idea as to how I could do that O_O. I have software class tomorrow though and my teacher is a physics teacher so maybe he can help, lol. Either way, do you think using the hypotenuse as the trajectory path would work? Know any methods to do it? Thanks. - Anteara

It's all triangles and depending on the angle of the ball, it may be more than one. In the diagram you drew, you don't care about the length of any hypotenuse but rather the height of the rightmost leg of the rightmost triangle because that's where the paddle wants to be (as your diagram also shows). - MSW

Ah, I see, so If I could calculate the angle (which I can, i have a variable called offset which creates the angle), I could use the angle to calculate where the ball will hit. Hmm, so, it seems like When I hit the ball, I'm looking for the angle of depression or elevation and I basically need to calculate how high that depression or elevation is in y pixels? - Anteara

Correct. "There is no royal road to geometry." — Euclides (I suck at trig, too, and have to re-learn it every time I need to use it) - MSW

I recommend against "AI cheating" methods such as increasing the paddle size. It is not well received by players. It would be better to have the AI play like a human and just sometimes miss by small random factor. - user774340

My advice is to use trig to work out where the ball will be, and have the paddle move there to intercept it.

Animation will be smooth at 30 frames per second.

When making a game AI is quite important these days that the player does not see it 'cheating' and giving it a larger paddle, or the ability to teleport, would be obvious signs of this. It should play in the same manner as a human, just better - not using some mechanism the player has no access to. "This game sucks because the CPU cheats" is a very common negative comment on videogame forums.

So if you need the computer to miss, make sure its trig calculations are off by a random factor, so the player cna't distinguish its play from a human's.

edit: For example: If random (X) <= speed of ball, then intercept correctly, otherwise miss by random (Y) units.

Respondido 30 Jul 12, 14:07

Thanks for your tips, and I appreciate what you said about AI cheating. I understand that that can get annoying. I'll avoid AI cheating, the only thing that seems kind of like that is that it's obvious when playing the game that the NPC paddle is trying to follow the slope of the triangle. But it's not perfect, which is what I want. - Anteara

Thanks for your help guys, I was able to work it out with my teacher based on your help and his.

He developed this algorithm (tbh he did it so fast that I wasn't really able to comprehend it!) but this essentially uses trig to generate where the paddle will go (Note, I don't use the angle as I have other values that can be used)

def AIController(self, *args):      
    ballsFuturePos = self.ball.center_y + ((self.width - self.ball.center_x) / self.ball.velocity_x) * self.ball.velocity_y
    numIterations = ((self.width - self.ball.center_x) / self.ball.velocity_x)
    #print ballsFuturePos
    #print numIterations
    if numIterations > 0:
        self.wantedPos = self.player2.center_y +(ballsFuturePos - self.player2.center_y) / numIterations        
        #print wantedPos
        #self.player2.center_y = wantedPos + (error / wantedPos) * 100

    if self.player2.center_y < self.wantedPos:
        self.player2.center_y = self.player2.center_y + 9
    if self.player2.center_y > self.wantedPos:
        self.player2.center_y = self.player2.center_y - 9

So I generate where the ball is going to hit the rightmost part of the screen by getting the balls y position, adding the (width - the x position of the ball) (which gives me how far until the rightmost part of the screen is in x pixels), then I didivde that by the x velocity, and times the whole thing by the y velocity, which gives me the slope (I think), and as such now that the slope is calculated it also means It has the trajectory and as such can predict where the ball will hit the screen.

I calculate the number of iterations needed to get to the ball by taking the width of screen and minusing it by the balls x position, which calculates how far it is until the rightmost part of the screen, and divide that by the velocity. This gives me a number that I can use to iterate.

now I iterate with this, and create a variable called wantedPos which is where I want my paddle to go. This uses the paddles y position, adding (where the ball will be - where the paddle is), which gives me the distance between the ball and the paddle, divided by the number of iterations, which gives me the position the paddle will be to be at the same position at the ball. As numIterations decreases in each call, so does the wantedPos, meaning the gap gets smaller. I then iterate the paddle closer to the ball, using 9 as a speed, which allows me to increase or decrease difficulty.

Thanks. If I messed up any of my logic in trying to describe his actions, please tell! I think I understand it but a confirmation would be nice :)

Respondido 31 Jul 12, 05:07

+1 self answers are good for SO. Confirmation? Does it work? My guess is it does, and that should be the confirmation that you need. I hadn't thought of it the way your teacher did, but trig is just about the ratios of triangle sides to each other and that's what the slopes are giving you. After all, you are just placing a paddle, not building bridges so approximations are close enough. Good thing that I'm not a game programmer. - MSW

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.