pythonkivygame-physicsponggame-ai

Increasing sprite position incrementally very fast without lag - 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.


Solution

  • 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.