Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/objects/controllers/PongAI.cc @ 2872

Last change on this file since 2872 was 2872, checked in by landauf, 15 years ago

some small adjustments in PongAI and related classes

  • Property svn:eol-style set to native
File size: 8.0 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "OrxonoxStableHeaders.h"
30#include "PongAI.h"
31
32#include "core/CoreIncludes.h"
33#include "core/ConfigValueIncludes.h"
34#include "objects/worldentities/ControllableEntity.h"
35#include "objects/worldentities/PongBall.h"
36#include "tools/Timer.h"
37
38namespace orxonox
39{
40    CreateUnloadableFactory(PongAI);
41
42    const static float MAX_REACTION_TIME = 0.4;
43
44    PongAI::PongAI(BaseObject* creator) : Controller(creator)
45    {
46        RegisterObject(PongAI);
47
48        this->ball_ = 0;
49        this->ballDirection_ = Vector2::ZERO;
50        this->ballEndPosition_ = 0;
51        this->randomOffset_ = 0;
52        this->relHysteresisOffset_ = 0.02;
53        this->strength_ = 0.5;
54        this->movement_ = 0;
55
56        this->setConfigValues();
57    }
58
59    PongAI::~PongAI()
60    {
61        for (std::list<std::pair<Timer<PongAI>*, char> >::iterator it = this->reactionTimers_.begin(); it != this->reactionTimers_.end(); ++it)
62            delete (*it).first;
63    }
64
65    void PongAI::setConfigValues()
66    {
67        SetConfigValue(strength_, 0.5).description("A value from 0 to 1 where 0 is weak and 1 is strong.");
68    }
69
70    void PongAI::tick(float dt)
71    {
72        if (!this->ball_ || !this->getControllableEntity())
73            return;
74
75        Vector3 mypos = this->getControllableEntity()->getPosition();
76        Vector3 ballpos = this->ball_->getPosition();
77        Vector3 ballvel = this->ball_->getVelocity();
78        float hysteresisOffset = this->relHysteresisOffset_ * this->ball_->getFieldDimension().y;
79
80        char move = 0;
81
82        // Check in which direction the ball is flying
83        if ((mypos.x > 0 && ballvel.x < 0) || (mypos.x < 0 && ballvel.x > 0))
84        {
85            // The ball is flying away
86            this->ballDirection_.x = -1;
87            this->ballDirection_.y = 0;
88
89            // Move to the middle
90            if (mypos.z > hysteresisOffset)
91                move = 1;
92            else if (mypos.z < -hysteresisOffset)
93                move = -1;
94        }
95        else if (ballvel.x == 0)
96        {
97            // The ball is standing still
98            this->ballDirection_.x = 0;
99            this->ballDirection_.y = 0;
100        }
101        else
102        {
103            // The ball is approaching
104            if (this->ballDirection_.x != 1)
105            {
106                // The ball just startet to approach, initialize all values
107                this->ballDirection_.x = 1;
108                this->ballDirection_.y = sgn(ballvel.z);
109                this->ballEndPosition_ = 0;
110                this->randomOffset_ = 0;
111
112                this->calculateRandomOffset();
113                this->calculateBallEndPosition();
114            }
115
116            if (this->ballDirection_.y != sgn(ballvel.z))
117            {
118                // The ball just bounced from a bound, recalculate the predicted end position
119                this->ballDirection_.y = sgn(ballvel.z);
120
121                this->calculateBallEndPosition();
122            }
123
124            // Move to the predicted end position with an additional offset (to hit the ball with the side of the bat)
125            float desiredZValue = this->ballEndPosition_ + this->randomOffset_;
126
127            if (mypos.z > desiredZValue + hysteresisOffset * (this->randomOffset_ < 0))
128                move = 1;
129            else if (mypos.z < desiredZValue - hysteresisOffset * (this->randomOffset_ > 0))
130                move = -1;
131        }
132
133        this->move(move);
134        this->getControllableEntity()->moveFrontBack(this->movement_);
135    }
136
137    void PongAI::calculateRandomOffset()
138    {
139        // Calculate the exponent for the position-formula
140        float exp = pow(10, 1 - 2*this->strength_); // strength: 0   -> exp = 10
141                                                    // strength: 0.5 -> exp = 1
142                                                    // strength: 1   -> exp = 0.1
143
144        // Calculate the relative position where to hit the ball with the bat
145        float position = pow(rnd(), exp); // exp > 1 -> position is more likely a small number
146                                          // exp < 1 -> position is more likely a large number
147
148        // The position shouln't be larger than 0.5 (50% of the bat-length from the middle is the end)
149        position *= 0.48;
150
151        // Both sides are equally probable
152        position *= rndsgn();
153
154        // Calculate the offset in world units
155        this->randomOffset_ = position * this->ball_->getBatLength() * this->ball_->getFieldDimension().y;
156    }
157
158    void PongAI::calculateBallEndPosition()
159    {
160        Vector3 position = this->ball_->getPosition();
161        Vector3 velocity = this->ball_->getVelocity();
162        Vector2 dimension = this->ball_->getFieldDimension();
163
164        // calculate end-height: current height + slope * distance
165        this->ballEndPosition_ = position.z + velocity.z / velocity.x * (-position.x + dimension.x / 2 * sgn(velocity.x));
166
167        // Calculate bounces
168        for (float limit = 0.35; limit < this->strength_ || this->strength_ > 0.99; limit += 0.4)
169        {
170            // Bounce from the upper bound
171            if (this->ballEndPosition_ > dimension.y / 2)
172            {
173                // Mirror the predicted position at the upper bound and add some random error
174                this->ballEndPosition_ = dimension.y - this->ballEndPosition_ + (rnd(-1, 1) * dimension.y * (1 - this->strength_));
175                continue;
176            }
177            // Bounce from the upper bound
178            if (this->ballEndPosition_ < -dimension.y / 2)
179            {
180                // Mirror the predicted position at the lower bound and add some random error
181                this->ballEndPosition_ = -dimension.y - this->ballEndPosition_ + (rnd(-1, 1) * dimension.y * (1 - this->strength_));
182                continue;
183            }
184            // No bounce - break
185            break;
186        }
187    }
188
189    void PongAI::move(char direction)
190    {
191        // The current direction is either what we're doing right now (movement_) or what is last in the queue
192        char currentDirection = this->movement_;
193        if (this->reactionTimers_.size() > 0)
194            currentDirection = this->reactionTimers_.back().second;
195
196        // Only add changes of direction
197        if (direction == currentDirection)
198            return;
199
200        // Calculate delay, but only to change direction or start moving (stop works without delay)
201        if (direction != 0)
202        {
203            float delay = MAX_REACTION_TIME * (1 - this->strength_);
204
205            // Add a new Timer
206            Timer<PongAI>* timer = new Timer<PongAI>(delay, false, this, createExecutor(createFunctor(&PongAI::delayedMove)));
207            this->reactionTimers_.push_back(std::pair<Timer<PongAI>*, char>(timer, direction));
208        }
209        else
210        {
211            this->movement_ = 0;
212        }
213    }
214
215    void PongAI::delayedMove()
216    {
217        // Get the new movement direction from the timer list
218        this->movement_ = this->reactionTimers_.front().second;
219
220        // Destroy the timer and remove it from the list
221        Timer<PongAI>* timer = this->reactionTimers_.front().first;
222        delete timer;
223
224        this->reactionTimers_.pop_front();
225    }
226}
Note: See TracBrowser for help on using the repository browser.