Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 #include <interactive_markers/interactive_marker_server.h>
00032
00033 #include <ros/ros.h>
00034 #include <math.h>
00035 #include <boost/thread/mutex.hpp>
00036
00037 using namespace visualization_msgs;
00038
00039 static const float FIELD_WIDTH = 12.0;
00040 static const float FIELD_HEIGHT = 8.0;
00041 static const float BORDER_SIZE = 0.5;
00042 static const float PADDLE_SIZE = 2.0;
00043 static const float UPDATE_RATE = 1.0 / 30.0;
00044 static const float PLAYER_X = FIELD_WIDTH * 0.5 + BORDER_SIZE;
00045 static const float AI_SPEED_LIMIT = 0.25;
00046
00047
00048 class PongGame
00049 {
00050 public:
00051
00052 PongGame() :
00053 server_("pong", "", false),
00054 last_ball_pos_x_(0),
00055 last_ball_pos_y_(0)
00056 {
00057 player_contexts_.resize(2);
00058
00059 makeFieldMarker();
00060 makePaddleMarkers();
00061 makeBallMarker();
00062
00063 reset();
00064 updateScore();
00065
00066 ros::NodeHandle nh;
00067 game_loop_timer_ = nh.createTimer(ros::Duration(UPDATE_RATE), boost::bind( &PongGame::spinOnce, this ) );
00068 }
00069
00070 private:
00071
00072
00073 void spinOnce()
00074 {
00075 if ( player_contexts_[0].active || player_contexts_[1].active )
00076 {
00077 float ball_dx = speed_ * ball_dir_x_;
00078 float ball_dy = speed_ * ball_dir_y_;
00079
00080 ball_pos_x_ += ball_dx;
00081 ball_pos_y_ += ball_dy;
00082
00083
00084 float t = 0;
00085 if ( reflect ( ball_pos_y_, last_ball_pos_y_, FIELD_HEIGHT * 0.5, t ) )
00086 {
00087 ball_pos_x_ -= t * ball_dx;
00088 ball_pos_y_ -= t * ball_dy;
00089
00090 ball_dir_y_ *= -1.0;
00091
00092 ball_dx = speed_ * ball_dir_x_;
00093 ball_dy = speed_ * ball_dir_y_;
00094 ball_pos_x_ += t * ball_dx;
00095 ball_pos_y_ += t * ball_dy;
00096 }
00097
00098 int player = ball_pos_x_ > 0 ? 1 : 0;
00099
00100
00101 if ( fabs(last_ball_pos_x_) < FIELD_WIDTH * 0.5 &&
00102 fabs(ball_pos_x_) >= FIELD_WIDTH * 0.5 )
00103 {
00104
00105 if ( ball_pos_y_ > player_contexts_[player].pos - PADDLE_SIZE * 0.5 - 0.5*BORDER_SIZE &&
00106 ball_pos_y_ < player_contexts_[player].pos + PADDLE_SIZE * 0.5 + 0.5*BORDER_SIZE )
00107 {
00108 reflect ( ball_pos_x_, last_ball_pos_x_, FIELD_WIDTH * 0.5, t );
00109 ball_pos_x_ -= t * ball_dx;
00110 ball_pos_y_ -= t * ball_dy;
00111
00112
00113 float offset = (ball_pos_y_ - player_contexts_[player].pos) / PADDLE_SIZE;
00114
00115 ball_dir_x_ *= -1.0;
00116 ball_dir_y_ += offset*2.0;
00117
00118 normalizeVel();
00119
00120
00121 if ( fabs(ball_dir_y_) > 0.707106781 )
00122 {
00123 ball_dir_x_ = ball_dir_x_ > 0.0 ? 1.0 : -1.0;
00124 ball_dir_y_ = ball_dir_y_ > 0.0 ? 1.0 : -1.0;
00125 normalizeVel();
00126 }
00127
00128 ball_dx = speed_ * ball_dir_x_;
00129 ball_dy = speed_ * ball_dir_y_;
00130 ball_pos_x_ += t * ball_dx;
00131 ball_pos_y_ += t * ball_dy;
00132 }
00133 }
00134
00135
00136 if ( fabs(ball_pos_x_) >= FIELD_WIDTH * 0.5 + 1.5*BORDER_SIZE )
00137 {
00138 reflect ( ball_pos_x_, last_ball_pos_x_, FIELD_WIDTH * 0.5 + 1.5*BORDER_SIZE, t );
00139 ball_pos_x_ -= t * ball_dx;
00140 ball_pos_y_ -= t * ball_dy;
00141 updateBall();
00142
00143 player_contexts_[1-player].score++;
00144 updateScore();
00145
00146 server_.applyChanges();
00147 reset();
00148 ros::Duration(1.0).sleep();
00149 }
00150 else
00151 {
00152 updateBall();
00153 }
00154
00155 last_ball_pos_x_ = ball_pos_x_;
00156 last_ball_pos_y_ = ball_pos_y_;
00157
00158
00159 if ( !player_contexts_[0].active || !player_contexts_[1].active )
00160 {
00161 int player = player_contexts_[0].active ? 1 : 0;
00162 float delta = ball_pos_y_ - player_contexts_[player].pos;
00163
00164 if ( delta > AI_SPEED_LIMIT ) delta = AI_SPEED_LIMIT;
00165 if ( delta < -AI_SPEED_LIMIT ) delta = -AI_SPEED_LIMIT;
00166 setPaddlePos( player, player_contexts_[player].pos + delta );
00167 }
00168
00169 speed_ += 0.0003;
00170 }
00171
00172 server_.applyChanges();
00173 }
00174
00175 void setPaddlePos( unsigned player, float pos )
00176 {
00177 if ( player > 1 )
00178 {
00179 return;
00180 }
00181
00182
00183 if ( pos > (FIELD_HEIGHT - PADDLE_SIZE) * 0.5 )
00184 {
00185 pos = (FIELD_HEIGHT - PADDLE_SIZE) * 0.5;
00186 }
00187 if ( pos < (FIELD_HEIGHT - PADDLE_SIZE) * -0.5 )
00188 {
00189 pos = (FIELD_HEIGHT - PADDLE_SIZE) * -0.5;
00190 }
00191
00192 player_contexts_[player].pos = pos;
00193
00194 geometry_msgs::Pose pose;
00195 pose.position.x = (player == 0) ? -PLAYER_X : PLAYER_X;
00196 pose.position.y = pos;
00197
00198 std::string marker_name = (player == 0) ? "paddle0" : "paddle1";
00199 server_.setPose( marker_name, pose );
00200 server_.setPose( marker_name+"_display", pose );
00201 }
00202
00203 void processPaddleFeedback( unsigned player, const visualization_msgs::InteractiveMarkerFeedbackConstPtr &feedback )
00204 {
00205 if ( player > 1 )
00206 {
00207 return;
00208 }
00209
00210 std::string control_marker_name = feedback->marker_name;
00211 geometry_msgs::Pose pose = feedback->pose;
00212
00213 setPaddlePos( player, pose.position.y );
00214
00215 if ( feedback->event_type == visualization_msgs::InteractiveMarkerFeedback::MOUSE_DOWN )
00216 {
00217 player_contexts_[player].active = true;
00218 }
00219 if ( feedback->event_type == visualization_msgs::InteractiveMarkerFeedback::MOUSE_UP )
00220 {
00221 player_contexts_[player].active = false;
00222 }
00223 }
00224
00225
00226 void reset()
00227 {
00228 speed_ = 6.0 * UPDATE_RATE;
00229 ball_pos_x_ = 0.0;
00230 ball_pos_y_ = 0.0;
00231 ball_dir_x_ = ball_dir_x_ > 0.0 ? 1.0 : -1.0;
00232 ball_dir_y_ = rand() % 2 ? 1.0 : -1.0;
00233 normalizeVel();
00234 }
00235
00236
00237 void normalizeVel()
00238 {
00239 float l = sqrt( ball_dir_x_*ball_dir_x_ + ball_dir_y_*ball_dir_y_ );
00240 ball_dir_x_ /= l;
00241 ball_dir_y_ /= l;
00242 }
00243
00244
00245
00246
00247
00248 bool reflect( float &pos, float last_pos, float limit, float &t )
00249 {
00250 if ( pos > limit )
00251 {
00252 t = (pos - limit) / (pos - last_pos);
00253 return true;
00254 }
00255 if ( -pos > limit )
00256 {
00257 t = (-pos - limit) / (last_pos - pos);
00258 return true;
00259 }
00260 return false;
00261 }
00262
00263
00264 void updateBall()
00265 {
00266 geometry_msgs::Pose pose;
00267 pose.position.x = ball_pos_x_;
00268 pose.position.y = ball_pos_y_;
00269 server_.setPose( "ball", pose );
00270 }
00271
00272
00273 void updateScore()
00274 {
00275 InteractiveMarker int_marker;
00276 int_marker.header.frame_id = "base_link";
00277 int_marker.name = "score";
00278
00279 InteractiveMarkerControl control;
00280 control.always_visible = true;
00281
00282 Marker marker;
00283 marker.type = Marker::TEXT_VIEW_FACING;
00284 marker.color.r = 1.0;
00285 marker.color.g = 1.0;
00286 marker.color.b = 1.0;
00287 marker.color.a = 1.0;
00288 marker.scale.x = 1.5;
00289 marker.scale.y = 1.5;
00290 marker.scale.z = 1.5;
00291
00292 std::ostringstream s;
00293 s << player_contexts_[0].score;
00294 marker.text = s.str();
00295 marker.pose.position.y = FIELD_HEIGHT*0.5 + 4.0*BORDER_SIZE;
00296 marker.pose.position.x = -1.0 * ( FIELD_WIDTH * 0.5 + BORDER_SIZE );
00297 control.markers.push_back( marker );
00298
00299 s.str("");
00300 s << player_contexts_[1].score;
00301 marker.text = s.str();
00302 marker.pose.position.x *= -1;
00303 control.markers.push_back( marker );
00304
00305 int_marker.controls.push_back( control );
00306
00307 server_.insert( int_marker );
00308 }
00309
00310 void makeFieldMarker()
00311 {
00312 InteractiveMarker int_marker;
00313 int_marker.header.frame_id = "base_link";
00314 int_marker.name = "field";
00315
00316 InteractiveMarkerControl control;
00317 control.always_visible = true;
00318
00319 Marker marker;
00320 marker.type = Marker::CUBE;
00321 marker.color.r = 1.0;
00322 marker.color.g = 1.0;
00323 marker.color.b = 1.0;
00324 marker.color.a = 1.0;
00325
00326
00327 marker.scale.x = FIELD_WIDTH + 6.0 * BORDER_SIZE;
00328 marker.scale.y = BORDER_SIZE;
00329 marker.scale.z = BORDER_SIZE;
00330 marker.pose.position.x = 0;
00331 marker.pose.position.y = FIELD_HEIGHT*0.5 + BORDER_SIZE;
00332 control.markers.push_back( marker );
00333
00334
00335 marker.pose.position.y *= -1;
00336 control.markers.push_back( marker );
00337
00338
00339 marker.scale.x = BORDER_SIZE;
00340 marker.scale.y = FIELD_HEIGHT + 3.0*BORDER_SIZE;
00341 marker.scale.z = BORDER_SIZE;
00342 marker.pose.position.x = FIELD_WIDTH*0.5 + 2.5*BORDER_SIZE;
00343 marker.pose.position.y = 0;
00344 control.markers.push_back( marker );
00345
00346
00347 marker.pose.position.x *= -1;
00348 control.markers.push_back( marker );
00349
00350
00351 int_marker.controls.push_back( control );
00352 server_.insert( int_marker );
00353 }
00354
00355 void makePaddleMarkers()
00356 {
00357 InteractiveMarker int_marker;
00358 int_marker.header.frame_id = "base_link";
00359
00360
00361 InteractiveMarkerControl control;
00362 control.always_visible = false;
00363 control.interaction_mode = InteractiveMarkerControl::MOVE_AXIS;
00364 control.orientation.w = 1;
00365 control.orientation.z = 1;
00366
00367
00368 Marker marker;
00369 marker.type = Marker::CUBE;
00370 marker.color.r = 1.0;
00371 marker.color.g = 1.0;
00372 marker.color.b = 1.0;
00373 marker.color.a = 0.0;
00374 marker.scale.x = BORDER_SIZE + 0.1;
00375 marker.scale.y = PADDLE_SIZE + 0.1;
00376 marker.scale.z = BORDER_SIZE + 0.1;
00377 marker.pose.position.z = 0;
00378 marker.pose.position.y = 0;
00379
00380 control.markers.push_back( marker );
00381
00382 int_marker.controls.push_back( control );
00383
00384
00385 int_marker.name = "paddle0";
00386 int_marker.pose.position.x = -PLAYER_X;
00387 server_.insert( int_marker );
00388 server_.setCallback( int_marker.name, boost::bind( &PongGame::processPaddleFeedback, this, 0, _1 ) );
00389
00390
00391 int_marker.name = "paddle1";
00392 int_marker.pose.position.x = PLAYER_X;
00393 server_.insert( int_marker );
00394 server_.setCallback( int_marker.name, boost::bind( &PongGame::processPaddleFeedback, this, 1, _1 ) );
00395
00396
00397 marker.scale.x = BORDER_SIZE;
00398 marker.scale.y = PADDLE_SIZE;
00399 marker.scale.z = BORDER_SIZE;
00400 marker.color.r = 0.5;
00401 marker.color.a = 1.0;
00402
00403 control.interaction_mode = InteractiveMarkerControl::NONE;
00404 control.always_visible = true;
00405
00406
00407 int_marker.name = "paddle0_display";
00408 int_marker.pose.position.x = -PLAYER_X;
00409
00410 marker.color.g = 1.0;
00411 marker.color.b = 0.5;
00412
00413 int_marker.controls.clear();
00414 control.markers.clear();
00415 control.markers.push_back( marker );
00416 int_marker.controls.push_back( control );
00417 server_.insert( int_marker );
00418
00419
00420 int_marker.name = "paddle1_display";
00421 int_marker.pose.position.x = PLAYER_X;
00422
00423 marker.color.g = 0.5;
00424 marker.color.b = 1.0;
00425
00426 int_marker.controls.clear();
00427 control.markers.clear();
00428 control.markers.push_back( marker );
00429 int_marker.controls.push_back( control );
00430 server_.insert( int_marker );
00431 }
00432
00433 void makeBallMarker()
00434 {
00435 InteractiveMarker int_marker;
00436 int_marker.header.frame_id = "base_link";
00437
00438 InteractiveMarkerControl control;
00439 control.always_visible = true;
00440
00441
00442 int_marker.name = "ball";
00443
00444 control.interaction_mode = InteractiveMarkerControl::NONE;
00445 control.orientation.w = 1;
00446 control.orientation.y = 1;
00447
00448 Marker marker;
00449 marker.color.r = 1.0;
00450 marker.color.g = 1.0;
00451 marker.color.b = 1.0;
00452 marker.color.a = 1.0;
00453 marker.type = Marker::CYLINDER;
00454 marker.scale.x = BORDER_SIZE;
00455 marker.scale.y = BORDER_SIZE;
00456 marker.scale.z = BORDER_SIZE;
00457 control.markers.push_back( marker );
00458
00459 int_marker.controls.push_back( control );
00460
00461 server_.insert( int_marker );
00462 }
00463
00464 interactive_markers::InteractiveMarkerServer server_;
00465
00466 ros::Timer game_loop_timer_;
00467
00468 InteractiveMarker field_marker_;
00469
00470 struct PlayerContext
00471 {
00472 PlayerContext(): pos(0),active(false),score(0) {}
00473 float pos;
00474 bool active;
00475 int score;
00476 };
00477
00478 std::vector<PlayerContext> player_contexts_;
00479
00480 float last_ball_pos_x_;
00481 float last_ball_pos_y_;
00482
00483 float ball_pos_x_;
00484 float ball_pos_y_;
00485
00486 float ball_dir_x_;
00487 float ball_dir_y_;
00488 float speed_;
00489 };
00490
00491
00492
00493 int main(int argc, char** argv)
00494 {
00495 ros::init(argc, argv, "pong");
00496
00497 PongGame pong_game;
00498 ros::spin();
00499 ROS_INFO("Exiting..");
00500 }