double x2; double y2; double invx1; // x速度不是通过质心贡献的, 即赞美x1构成速度的向量 double invy1; double invx2; double invy2; double xdist1 = MoveList [a] - > x + 步* MoveList [a] - > x_change / step; double ydist1 = MoveList [a] - > y + steps * MoveList [a] - > y_change /步骤; double xdist2 = MoveList [b] - > x + step * MoveList [b] - > x_change / step; double ydist2 = MoveList [b] - > y + step * MoveList [b] - > y_change / step; if(xdist1 == xdist2)//防止除以0 { x1 = 0; y1 = MoveList [a] - > ; y_change; invx1 = 0; invy1 = 0; x2 = 0; y2 = MoveList [b] - > y_change; invx2 = 0; invy2 = 0; } else if(ydist1 == ydist2) { x1 = MoveLis t [a] - > x_change; y1 = 0; invx1 = 0; invy1 = 0; x2 = MoveList [b] - > x_change; y2 = 0; invx2 = 0; invy2 = 0; } else { x1 =( (xdist1 + MoveList [a] - > x_change)*((xdist1-xdist2)/(ydist2-ydist1)) - MoveList [a] - > y_change - xdist1 *((ydist2-ydist1)/(xdist2-xdist1)))/( (xdist1-xdist2)/(ydist2-ydist1) - (ydist2-ydist1)/(xdist2-xdist1 )); y1 =(x1 *(ydist2-ydist1)/(xdist2-xdist1)) - xdist1 * (ydist2-ydist1)/(xdist2-xdist1) + ydist1; invx1 =(xdist1 + MoveList [a] - > x_change) - x1; invy1 =(ydist1 + MoveList [a] - > y_change) - y1; x2 =((xdist2 + MoveList [b] - > x_change)*((xdist2-xdist1)/ (ydist1-ydist2)) - MoveList [b] - > y_change - xdist2 *((ydist1-ydist2)/(xdist1-xdist2)))/( (xdist2-xdist1)/(ydist1-ydist2) - (ydi st1-ydist2)/(xdist1-xdist2)); y2 =(x2 *(ydist1-ydist2)/(xdist1-xdist2)) - xdist2 * (ydist1 -ydist2)/(xdist1-xdist2)+ ydist2; invx2 =(xdist2 + MoveList [b] - > x_change) - x2; invy2 =(ydist2 + MoveList [b] - > y_change) - y2; x1 - = xdist1; //它的坐标只相对于它的 位置,让我们给它们一个绝对值,让它们成为 的变化量,使它们变得毫无用处 y1 - = ydist1; x2 - = xdist2; y2 - = ydist2; } if(MoveList [a] - > controller()== physics)//只改变它的 速度,如果它的一个对象操纵像那个 { MoveList [a] - > x_change = invx1 + energy * NewVel(x1,x2, MoveList [a] - >get_mass(), MoveList[b]->get_mass()); // we want to take its initial velocity and change it (according to the masses) by the impact of the 2nd object MoveList[a]->y_change = invy1 + energy * NewVel(y1,y2, MoveList[a]->get_mass(), MoveList[b]->get_mass()); HitTable(MoveList[a], 0); // if the object has been hit by another object and hits the wall as well, its going to be in a fast rebound for many iterations, but physically, the mallet would absorb the energy, thus passing 0.0 so the velocity is nothing } if (MoveList[b]->controller() == physics) { MoveList[b]->x_change = invx2 + energy * NewVel(x2,x1, MoveList[b]->get_mass(), MoveList[a]->get_mass()); MoveList[b]->y_change = invy2 + energy * NewVel(y2,y1, MoveList[b]->get_mass(), MoveList[a]->get_mass()); HitTable(MoveList[b], 0); } } } for (int i=0; i<MoveList.size(); ++i) if (MoveList[i]->controller() == physics) HitTable(MoveList[i], energy); return; } void Reset() { double leeway = 0.02; // amount that the reset function allows the puck to be away from the wall, this converts to 0.5cm for (int i=0; i < MoveList.size(); ++i) if (MoveList[i]->controller() == physics) // a puck if ( Over( MoveList[i]->next_x(), width/2-rest - MoveList[i]->get_radius() - leeway )) if ( Over( MoveList[i]->next_y(), length/2-rest - MoveList[i]->get_radius() - leeway )) // only check your side, cant reset on the other side if ( sqrt( pow(MoveList[i]->change_x(), 2.0) + pow(MoveList[i]->change_y(), 2.0) ) < leeway) // if total velocity is almost nothing MoveList[i]->Replace(); return; } void NextFrame() { Reset(); Collide(); for (int i=0; i < MoveList.size(); ++i) MoveList[i]->Move(); return; } [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated.第一次海报:做到这一点! ]Hi, I''ve just taught myself C++, so I haven''t learnt much about style orthe like from any single source, and I''m quite styleless as a result.But at the same time, I really want nice code and I go to greatlengths to restructure my code just to look concise and make it moremanageable.When I say this, I''m also referring to the way I write my functions.It seems to me sometimes that I shouldn''t have many void functionsaccepting addresses and changing their values for example, but rathera double function that returns the calculated value. i.e.void Change(Type& variable);should be replaced withType Change(Type variable); I just don''t know what''s better code (in terms of speed, style, etc.). I''m moving into a more mature phase of my program, and before Icontinue I would like jsut a final verdict on my code. I''m looking forcomments about whether my functions should be more simple (i.e. toomuch happens in each), whether I should rather return value instead ofalter through references passed, whether my general style isinelegant. I''m also concerned about my class structures. But I''ll showyou _some_ code and let you decide. Please note that I''m not asking you to go through the actual logic ofall this code, but rather just calls to functions, classes and thelike. Thanks. Davin. //---------------------//Globals.cpp - just some global variables needed by most functions#include <vector>#include "GLFiles.h"#include "Derived.h" using std::vector; //quadratic to draw cylinders, spheres etc.GLUquadricObj *quadratic; //window dimensionsintWIDTH = 1024;intHEIGHT = 768; //table dimensionsfloat length = 25.15f; // 251.5cmfloat width = 13.84f;float height = 8.13f;float goal = 4.0f; float rest = 1.5f; // width between esdge and tabletop // Misc int step = 150; // number of steps the collision detection willdeconstruct each translation into in checking for a collision Mallet* Mallet1;Mallet* Mallet2;vector <Puck*> Pucks; double friction = 0.985;int mouse_sens = 4; // 4-10, 10-least sensitive //---------------------//Abstract.h - two abstract classes, some derived classes will beinherited from both#ifndef Abstract_Classes#define Abstract_Classes #include "Globals.h" using namespace std; class Movable;class Drawable; void AddDraw(Drawable* draw);void AddMove(Movable* move); extern double friction; class Drawable{ public: Drawable() {AddDraw(this);} virtual void Draw() = 0; }; class Movable{ public: Movable(double x1, double y1, double r, Controller cont) { AddMove(this); control = cont; x = x1;y = y1; x_change = 0.0;y_change = 0.0; radius = r; } double next_x() { return x + friction * x_change; }double next_y() { return y + friction * y_change; } double change_x() { return x_change; }double change_y() { return y_change; } double now_x() { return x; }double now_y() { return y; } double get_radius() { return radius; }double get_mass() { return mass; } Controller controller() { return control; } friend void Collide();friend void HitTable(Movable* obj, double change); void Replace(); // reset its position (after a goal for example) andvelocity virtual void Move() = 0; protected: Controller control; double radius;double mass; //used as a comparison between inherited types (as wellas the physics) double x;double y; double x_change;double y_change; }; #endif //------------------//Derived.h - two derived classes#ifndef Derived_Classes#define Derived_Classes #include "Abstract.h" class Mallet: public Drawable, public Movable{ public: Mallet(double x, double y, Controller cont): Movable(x,y, 0.76, cont){mass = 3; // 3kg} void Draw();void Move(); }; class Puck: public Drawable, public Movable{ public: Puck(double x, double y): Movable(x,y, 0.7, physics) {mass = 0.03; // 30g} void Draw();void Move(); }; #endif //----------------//Derived.cpp - the functions for Derived.h#include <vector>#include <cmath> #include "GLFiles.h"#include "Derived.h" #include "GeoMotion.h" using namespace std; //MalletXXX works when a mallet isnt hitting a XXXvoid MalletWall(GeoMotion& coords); // if the object has hit a wall,it returns the appropriate variable, else it returns the same variablevaluevoid MalletSquash(GeoMotion& coords, Mallet* mal); // has it hit asquashes puck against the wall (for example)?void MalletBound(GeoMotion& coords, Mallet* mal); // if its in yourhalf - y includes the position + change + radius GeoMotion MouseCont(Mallet* mal); // mouse controlGeoMotion AICont(Mallet* mal); // AI control bool Over(double coord, double fixed); extern GLUquadricObj *quadratic; extern vector<Drawable *> DrawList;extern vector<Movable *> MoveList; extern float length;extern float width;extern float rest; extern int step; // to disect the mallets movement if its going intoanother object that is unmovable extern vector<Puck*> Pucks; extern double friction;extern int mouse_sens; extern int WIDTH;extern int HEIGHT; void Puck::Draw() { glPushMatrix(); glColor3f(0.0f, 0.0f, 0.0f); glTranslatef(x, 0.0f, y);glRotatef(90, 1.0f, 0.0f, 0.0f); gluCylinder(quadratic, radius, radius,0.1f,32,32); // main outsidegluDisk(quadratic,0.0f, radius,32,32); // disc covering topgluCylinder(quadratic, 0.2, 0.2,0.11f,32,32); // nice looking ring inthe middle glPopMatrix(); return; } void Puck::Move() { x_change *= friction;y_change *= friction; x += x_change;y += y_change; return; } void Mallet::Draw() { double OuterHeight = 0.25; // outer mallet heightdouble WallDown = 0.1; // how far down the base of the handle is fromthe outer heightdouble WallIn = 0.1; // how far in the outer wall comesdouble HHeight = 0.4; // handle heigthdouble HWidth = 0.6; glPushMatrix(); glColor3f(0.0f, 0.0f, 0.0f); glTranslatef(x, 0.0f, y);glRotatef(-90, 1.0f, 0.0f, 0.0f); gluCylinder(quadratic, radius, radius, OuterHeight ,32,32); //outside wall glTranslatef(0.0f, 0.0f, OuterHeight);gluDisk(quadratic, radius-WallIn, radius,32,32); // top of the wall glTranslatef(0.0f, 0.0f, -WallDown); // has been rotated, so z axisis where y used to be, and -gluCylinder(quadratic, HWidth/2 , radius-WallIn, WallDown,32,32); //connection from handle to top of wallgluCylinder(quadratic, HWidth/2 , HWidth/2, HHeight ,32,32); //handle glTranslatef(0.0f, 0.0f, HHeight);gluSphere(quadratic, HWidth/2 ,32,32); // handle nob glPopMatrix(); return; } void Mallet::Move() { GeoMotion coords(this); if (control == mouse)coords = MouseCont(this);else if (control == AI)coords = AICont(this); x = coords.x;y = coords.y; x_change = coords.x_change;y_change = coords.y_change; return; } GeoMotion MouseCont(Mallet* mal) { GeoMotion coords(mal); POINT mouse;GetCursorPos(&mouse); // from windows.h double tmpX = mouse.x - WIDTH/2; // centre screen gives largecoordinates, but coordinates in the centre should be 0,0 - withoutthis, centre coords will be like 512,384double tmpY = mouse.y - HEIGHT/2; coords.x_change = (tmpX / WIDTH * width)/mouse_sens; // get it as afraction of the screen, then make that fraction relative to the tabledimension, then divide by the sensitivitycoords.y_change = (tmpY / HEIGHT * length)/mouse_sens; //continually send the coords to a function and have values updatedMalletWall(coords); // if its hitting a wall, return the change thatit should have to go max without hittin, else return current changeMalletBound(coords, mal);MalletSquash(coords, mal); coords.x = coords.x + coords.x_change; // make the x position itscurretn position plus its newly calculated changecoords.y = coords.y + coords.y_change; SetCursorPos(WIDTH/2, HEIGHT/2); return coords; } GeoMotion AICont(Mallet* mal) { GeoMotion coords(mal); coords.x_change = Pucks[0]->now_x() - coords.x;coords.y_change=0;MalletBound(coords,mal);coords.x += coords.x_change;coords.y += coords.y_change; return coords; } void MalletBound(GeoMotion& coords, Mallet* mal) { // bound to ownhalf double littlebit = 0.3;double bound = mal->controller() == mouse? 0 + Pucks[0]->get_radius()+littlebit : 0 - Pucks[0]->get_radius() -littlebit; // boundary,furthest forward it can go. 0 is halfway if (mal->controller() == mouse &&coords.y+coords.y_change-coords.radius <= bound)coords.y_change = bound - (coords.y-coords.radius);else if ( mal->controller() != mouse && mal->controller() != physics&& coords.y+coords.y_change+coords.radius >= bound)coords.y_change = bound - (coords.y+coords.radius); // make distanceto be just touching return; } void MalletSquash(GeoMotion& coords, Mallet* mal) { for (int i=0; i<MoveList.size(); ++i) // if its being squashedagainst a wall..{if (MoveList[i] != mal && MoveList[i]->controller() == physics) //if its not the same address (in which case of course they will collideand this will return false) and if its changeable on collision, like apuckif (sqrt( pow(coords.x+coords.x_change-MoveList[i]->next_x(), 2.0)+ pow(coords.y+coords.y_change-MoveList[i]->next_y(), 2.0) ) <coords.radius+MoveList[i]->get_radius()) // if (using distanceformula) its not hittign the object, if it is, their distance b/wradii will be less than sum of radiiif ( Over(coords.x+coords.x_change, width/2-rest-2*MoveList[i]->get_radius()-coords.radius) == true ||Over(coords.y+coords.y_change, length/2-rest-2*MoveList[i]->get_radius()-coords.radius) == true)for (int j=0; j<=step; ++j)if (sqrt( pow(coords.x+ j*coords.x_change/step-MoveList[i]->next_x(), 2.0) + pow(coords.y+ j*coords.y_change/step-MoveList[i]->next_y(), 2.0) ) <coords.radius+MoveList[i]->get_radius()){coords.x_change = (j-1)*coords.x_change/step; // take the oneright before collision, otherwise the object will bounce back INSIDEthe malletcoords.y_change = (j-1)*coords.y_change/step;}} return; } void MalletWall(GeoMotion& coords) { if (coords.x+coords.x_change + coords.radius > width/2-rest) // ifits going over the side wallcoords.x_change = width/2-rest -coords.radius - coords.x; else if (coords.x+coords.x_change - coords.radius < -width/2+rest)coords.x_change = -width/2+rest + coords.radius - coords.x; // makeit go right up to the wall if (coords.y+coords.y_change + coords.radius > length/2-rest) // ifits going over the front/back wallcoords.y_change = length/2-rest -coords.radius - coords.y; else if (coords.y+coords.y_change - coords.radius < -length/2+rest)coords.y_change = -length/2+rest + coords.radius - coords.y; // makeit go right up to the wall return; } void Movable::Replace() { x_change = 0;y_change = 0; x = 0; if (y<0) // if it needs to be reset on the other sidey = -length/4;elsey = length/4; double inity = y; bool overlap;do {overlap = false; for (int i=0; i< MoveList.size(); ++i)if (MoveList[i] != this && sqrt( pow(y - MoveList[i]->y, 2.0) +pow(x - MoveList[i]->x, 2.0) ) < radius + MoveList[i]->get_radius()){overlap = true; x >= 0? x=-x-radius/2 : x=-x; // if its on the right, switch it tothe left, if its on the left sidemove it one object diameter acrossand retry if (x < -width/2+rest + radius) // dont keep replacing it, socheck its not off the edge, but make sure you dont place it too close,at least a radius away{x = 0; y = y >= inity? y - 2*(y-inity) -radius/2 : y + 2*(inity-y); //switch sides each time, done by subtracting from y twice its distancefrom length/4 (starting point) thereby reflecting it by this point if (y < radius || y > -radius){y=inity;overlap = false; // we have placed it everywhere in the wholehalf and nothing works, pretend there nothing wrong and set it in agoal position to redo this procedure next iteration}} break;}} while (overlap == true); return; } void AddDraw(Drawable* draw) {DrawList.push_back(draw);return;} void AddMove(Movable* move) {MoveList.push_back(move);return;} //----------------//DrawMotion.cpp - handles functions like drawing each frame,collision detection, and how objects should reflect (both speed anddirection)#include <vector>#include <cmath> #include "Derived.h"#include "GLFiles.h" using namespace std; void SetupObj();bool LoadPucks(int pucks);bool LoadMallets();void ReplacePucks();void CloseGL();void DrawObj();bool Over(double coord, double fixed);int CircleCol(Movable* obj1, Movable* obj2);int FixedCol(Movable* obj, double x, double y);double GoalCoord(double coord, double fixed);void HitTable(Movable* obj, double change);double NewVel(double vel1, double vel2, double m1, double m2);void Collide();void Reset();void NextFrame(); extern Mallet* Mallet1;extern Mallet* Mallet2;extern vector<Puck*> Pucks; extern float length;extern float width;extern float height;extern float rest;extern float goal; extern int step; vector<Drawable *> DrawList;vector<Movable *> MoveList; void SetupObj() { ShowCursor(false); if (LoadPucks(1) != true || LoadMallets() != true) // LoadPucks firstbecause mallet AI is used to check where the puck is and it will movethe puck first this way, and the AI can checkexit(-1); ReplacePucks(); return; } bool LoadPucks(int pucks) { Puck* tmpPuck; for (int i=0; i<pucks; ++i){try {tmpPuck = new Puck(0,5);Pucks.push_back(tmpPuck);}catch(std::bad_alloc nomem){return false;}} return true; } bool LoadMallets() { try {Mallet* Mallet1 = new Mallet(0,6, mouse);Mallet* Mallet2 = new Mallet(0,1, AI);}catch(std::bad_alloc nomem){return false;} return true; } void ReplacePucks() { for (int i=0; i<Pucks.size(); ++i)Pucks[0]->Replace(); return; } void CloseGL() { ShowCursor(true); delete Mallet1;delete Mallet2; for (int i=0; i<Pucks.size(); ++i)delete Pucks[i]; return; } void DrawObj() { for (int i=0; i < DrawList.size(); ++i)DrawList[i]->Draw(); return; } bool Over(double coord, double fixed) { // has coord gone beyond fixedin both positive and negative dimensions if (coord > fixed || coord < -fixed)return true; return false; } int CircleCol(Movable* obj1, Movable* obj2) { // returns 0 if there nocollision, else it returns the step of the process where the collisionwas double xdist1 = obj1->now_x(); // its distance in the x directionafter any given number of steps through the process of deconstructiondouble ydist1 = obj1->now_y();double xdist2 = obj2->now_x();double ydist2 = obj2->now_y(); double tmpX1 = obj1->change_x();double tmpY1 = obj1->change_y();double tmpX2 = obj2->change_x();double tmpY2 = obj2->change_y(); for (int i=1; i<=step; ++i){if (Over(xdist1 +tmpX1/step, width/2-rest -obj1->get_radius()) ==true) // if any of the NEXT distances (thats why one step has beenadded) are beyond a boundary, change the directiontmpX1 *= -1;if (Over(ydist1 +tmpY1/step, length/2-rest -obj1->get_radius()) ==true && (xdist1+tmpX1/step >= goal/2 || xdist1+tmpX1/step <= -goal/2))tmpY1 *= -1;if (Over(xdist2 +tmpX2/step, width/2-rest -obj2->get_radius()) ==true && (xdist2+tmpX2/step >= goal/2 || xdist2+tmpX2/step <= -goal/2))tmpX2 *= -1;if (Over(ydist2 +tmpY2/step, length/2-rest -obj2->get_radius()) ==true)tmpY2 *= -1;// check this FIRST because the first step could step over aboundary first and this needs to check it before ?dist is set to setthe correct direction of tmp? xdist1 = obj1->now_x() + i * (tmpX1/step); // where it is + ifractions of the change, i.e. more of the fractionydist1 = obj1->now_y() + i * (tmpY1/step); xdist2 = obj2->now_x() + i * (tmpX2/step);ydist2 = obj2->now_y() + i * (tmpY2/step); if ( sqrt( pow(xdist1 - xdist2, 2.0) + pow(ydist1 - ydist2, 2.0) ) <obj1->get_radius()+obj2->get_radius() ) // distance between radii <the sum of the radiireturn i;} return 0; } int FixedCol(Movable* obj, double x, double y) { // returns 0 if thereno collision, else it returns the step of the process where thecollision was double xdist = obj->now_x(); // its distance in the x direction afterany given number of steps through the process of deconstructiondouble ydist = obj->now_y(); double tmpX = obj->change_x();double tmpY = obj->change_y(); for (int i=1; i<=step; ++i){if (Over(xdist +tmpX/step, width/2-rest -obj->get_radius()) == true)// if any of the NEXT distances (thats why one step has been added)are beyond a boundary, change the directiontmpX *= -1;if (Over(ydist +tmpY/step, length/2-rest -obj->get_radius()) == true&& (xdist+tmpX/step >= goal/2 || xdist+tmpX/step <= -goal/2) )tmpY *= -1;// check this FIRST because the first step could step over aboundary first and this needs to check it before ?dist is set to setthe correct direction of tmp? xdist = obj->now_x() + i * (tmpX/step); // where it is + i fractionsof the change, i.e. more of the fractionydist = obj->now_y() + i * (tmpY/step); if ( sqrt( pow(xdist - x, 2.0) + pow(ydist - y, 2.0) ) <obj->get_radius() ) // distance between radii < the sum of the radiireturn i;} return 0; } double GoalCoord(double coord, double fixed) { if (coord > 0)return fixed;elsereturn -fixed; } double NewVel(double vel1, double vel2, double m1, double m2) {return vel1 * (m1-m2) / (m1+m2) + vel2 * 2 * m2 / (m1+m2);} void HitTable(Movable* obj, double change) { if (Over(obj->next_y(), length/2-rest) && obj->next_x() <=goal/2-obj->get_radius() && obj->next_x() >=-goal/2+obj->get_radius()) // GOAL !! only reset its position when itsbeyond saving{obj->Replace();return; // no need to continue} if ( Over(obj->next_x(), width/2-rest - obj->radius) ) // If its hitthe side wallsobj->x_change *= -change; if ( Over(obj->next_y(), length/2-rest - obj->radius) ) // possiblyhit the front/back wall{if ( obj->next_x() >= goal/2 || obj->next_x() <= -goal/2 ) //hitting the actual wallobj->y_change *= -change; else // hitting the corner or its a goal{double gx = GoalCoord(obj->next_x(), goal/2); // goal xdouble gy = GoalCoord(obj->next_y(), length/2-rest); // goal y if ( int stepped = FixedCol(obj, gx, gy) ) // hits the corner ofthe goal because its distance from it is less than its radius{double xdist = obj->x + stepped * obj->x_change/step;double ydist = obj->y + stepped * obj->y_change/step; double x = ( -obj->y_change - xdist*(ydist-gy)/(xdist-gx) +(xdist+obj->x_change)*(gx-xdist)/(ydist-gy) ) / ((gx-xdist)/(ydist-gy) - (ydist-gy)/(xdist-gx) ); // effect of thecorner (vector)double y = ydist + x*(ydist-gy)/(xdist-gx) -xdist*(ydist-gy)/(xdist-gx); // y effect double invx = (xdist+obj->x_change) - x; // inverse(perpendicular) velocities calculated before points are made relative- otherwise the geometry is brokendouble invy = (ydist+obj->y_change) - y; x -= xdist;y -= ydist; obj->x_change = invx + change * -x; // corner just reverses themotion (very solid)obj->y_change = invy + change * -y;}}} return; } void Collide() { double energy = 0.9; // _energy_ is multiplied by the velocitiesafter a collision compensating for energy loss to sound, heat etc. int stepped; for (int a=0; a < MoveList.size(); ++a)for (int b=a+1; b < MoveList.size(); ++b) // only go through theones you havent already, because the first went through all, thesecond need only go from the 3RD onwards{if ( stepped = CircleCol(MoveList[a], MoveList[b]) ){double x1; // x velocity contributed via centre of mass, i.e. thevector that contributes to the change, this is the x coordinatedouble y1; double x2;double y2; double invx1; // x velocity NOT contributed via centre of mass,i.e. the vector that compliments x1 making up the velocitydouble invy1; double invx2;double invy2; double xdist1 = MoveList[a]->x +stepped*MoveList[a]->x_change/step;double ydist1 = MoveList[a]->y +stepped*MoveList[a]->y_change/step; double xdist2 = MoveList[b]->x +stepped*MoveList[b]->x_change/step;double ydist2 = MoveList[b]->y +stepped*MoveList[b]->y_change/step; if (xdist1 == xdist2) // prevent division by 0{x1 = 0;y1 = MoveList[a]->y_change; invx1 = 0;invy1 = 0; x2 = 0;y2 = MoveList[b]->y_change; invx2 = 0;invy2 = 0;}else if (ydist1 == ydist2){x1 = MoveList[a]->x_change;y1 = 0; invx1 = 0;invy1 = 0; x2 = MoveList[b]->x_change;y2 = 0; invx2 = 0;invy2 = 0;}else{x1 = ( (xdist1 +MoveList[a]->x_change)*((xdist1-xdist2)/(ydist2-ydist1)) -MoveList[a]->y_change - xdist1*((ydist2-ydist1)/(xdist2-xdist1)) ) / ((xdist1-xdist2)/(ydist2-ydist1) - (ydist2-ydist1)/(xdist2-xdist1) );y1 = (x1*(ydist2-ydist1)/(xdist2-xdist1)) - xdist1 *(ydist2-ydist1)/(xdist2-xdist1) + ydist1; invx1 = (xdist1+MoveList[a]->x_change) - x1;invy1 = (ydist1+MoveList[a]->y_change) - y1; x2 = ( (xdist2 +MoveList[b]->x_change)*((xdist2-xdist1)/(ydist1-ydist2)) -MoveList[b]->y_change - xdist2*((ydist1-ydist2)/(xdist1-xdist2)) ) / ((xdist2-xdist1)/(ydist1-ydist2) - (ydist1-ydist2)/(xdist1-xdist2) );y2 = (x2*(ydist1-ydist2)/(xdist1-xdist2)) - xdist2 *(ydist1-ydist2)/(xdist1-xdist2) + ydist2; invx2 = (xdist2+MoveList[b]->x_change) - x2;invy2 = (ydist2+MoveList[b]->y_change) - y2; x1 -= xdist1; // its co-ordinates are only relative to itsposition, lets give them an absolute value by making them be theamount of change, making where they were uselessy1 -= ydist1; x2 -= xdist2;y2 -= ydist2;} if (MoveList[a]->controller() == physics) // only change itsvelocity if its an object thats manipulated like that{MoveList[a]->x_change = invx1 + energy * NewVel(x1,x2,MoveList[a]->get_mass(), MoveList[b]->get_mass()); // we want to takeits initial velocity and change it (according to the masses) by theimpact of the 2nd objectMoveList[a]->y_change = invy1 + energy * NewVel(y1,y2,MoveList[a]->get_mass(), MoveList[b]->get_mass()); HitTable(MoveList[a], 0); // if the object has been hit byanother object and hits the wall as well, its going to be in a fastrebound for many iterations, but physically, the mallet would absorbthe energy, thus passing 0.0 so the velocity is nothing} if (MoveList[b]->controller() == physics){MoveList[b]->x_change = invx2 + energy * NewVel(x2,x1,MoveList[b]->get_mass(), MoveList[a]->get_mass());MoveList[b]->y_change = invy2 + energy * NewVel(y2,y1,MoveList[b]->get_mass(), MoveList[a]->get_mass()); HitTable(MoveList[b], 0);}}} for (int i=0; i<MoveList.size(); ++i)if (MoveList[i]->controller() == physics)HitTable(MoveList[i], energy); return; } void Reset() { double leeway = 0.02; // amount that the reset function allows thepuck to be away from the wall, this converts to 0.5cm for (int i=0; i < MoveList.size(); ++i)if (MoveList[i]->controller() == physics) // a puckif ( Over( MoveList[i]->next_x(), width/2-rest -MoveList[i]->get_radius() - leeway ))if ( Over( MoveList[i]->next_y(), length/2-rest -MoveList[i]->get_radius() - leeway )) // only check your side, cantreset on the other sideif ( sqrt( pow(MoveList[i]->change_x(), 2.0) +pow(MoveList[i]->change_y(), 2.0) ) < leeway) // if total velocity isalmost nothingMoveList[i]->Replace(); return;} void NextFrame() { Reset();Collide(); for (int i=0; i < MoveList.size(); ++i)MoveList[i]->Move(); return; } [ See http://www.gotw.ca/resources/clcm.htm for info about ][ comp.lang.c++.moderated. First time posters: Do this! ]推荐答案I am not subscribed to c.l.c++.m, so I’’m only replying in c.l.c++... "wired" <lu*****@hotmail.com> wrote...I am not subscribed to c.l.c++.m, so I''m only replying in c.l.c++... "wired" <lu*****@hotmail.com> wrote... I’’ve just taught myself C++, I hope you don’’t see it as an event. "I’’ve just spilled a bowl of soup on my trousers" or something like that... so I haven’’t learnt much about style or the like from any single source, and I’’m quite styleless as a result. It is expected. Having learned rules of chess one hasn’’t become a player yet. But at the same time, I really want nice code and I go to great lengths to restructure my code just to look concise and make it more manageable. When I say this, I’’m also referring to the way I write my functions. It seems to me sometimes that I shouldn’’t have many void functions accepting addresses and changing their values for example, but rather a double function that returns the calculated value. i.e. void Change(Type& variable); should be replaced with Type Change(Type variable); Some do think that a function, since it’’s name that, should only work like a mathematical namesake: give a value based on input parameters. That would introduce too much limitation, and even the standard library is not written that way. Keep that in mind. I just don’’t know what’’s better code (in terms of speed, style, etc.). Nobody knows. Style is a personal preference, speed is a feature of a finished program. Yes, there are algorithms that serve the same purpose and perform faster than others. Do use them if they produce code the goal of which is as easy to recognise as with the others. But don’’t concern yourself with speed too much before you finish the functionality. First make it work, then make it fast. You’’re right about making your code "maintainable" even if it means maintenance by you. Some programmers produce enough code to easily forget what they wrote a week before. They, like others would, go back and look at their own code trying to figure out why certain things were written in some way. So, for your own sake, do write easily maintainable code. I’’m moving into a more mature phase of my program, and before I continue I would like jsut a final verdict on my code. Isn’’t this a bit too risky? I mean, using "final verdict" when asking opinion on your code. If somebody says "it’’s crap", are you going to give up programming? Hell, no. If somebody says, "yeah, it’’ll work", are you going to stop improving yourself? I’’m looking for comments about whether my functions should be more simple (i.e. too much happens in each), whether I should rather return value instead of alter through references passed, whether my general style is inelegant. I’’m also concerned about my class structures. But I’’ll show you _some_ code and let you decide. Please note that I’’m not asking you to go through the actual logic of all this code, but rather just calls to functions, classes and the like. I''ve just taught myself C++,I hope you don''t see it as an event. "I''ve just spilled a bowlof soup on my trousers" or something like that... so I haven''t learnt much about style or the like from any single source, and I''m quite styleless as a result.It is expected. Having learned rules of chess one hasn''t becomea player yet. But at the same time, I really want nice code and I go to great lengths to restructure my code just to look concise and make it more manageable. When I say this, I''m also referring to the way I write my functions. It seems to me sometimes that I shouldn''t have many void functions accepting addresses and changing their values for example, but rather a double function that returns the calculated value. i.e. void Change(Type& variable); should be replaced with Type Change(Type variable);Some do think that a function, since it''s name that, should onlywork like a mathematical namesake: give a value based on inputparameters. That would introduce too much limitation, and eventhe standard library is not written that way. Keep that in mind. I just don''t know what''s better code (in terms of speed, style, etc.).Nobody knows. Style is a personal preference, speed is a featureof a finished program. Yes, there are algorithms that serve thesame purpose and perform faster than others. Do use them if theyproduce code the goal of which is as easy to recognise as with theothers. But don''t concern yourself with speed too much before youfinish the functionality. First make it work, then make it fast. You''re right about making your code "maintainable" even if it meansmaintenance by you. Some programmers produce enough code to easilyforget what they wrote a week before. They, like others would, goback and look at their own code trying to figure out why certainthings were written in some way. So, for your own sake, do writeeasily maintainable code. I''m moving into a more mature phase of my program, and before I continue I would like jsut a final verdict on my code.Isn''t this a bit too risky? I mean, using "final verdict" whenasking opinion on your code. If somebody says "it''s crap", areyou going to give up programming? Hell, no. If somebody says,"yeah, it''ll work", are you going to stop improving yourself? I''m looking for comments about whether my functions should be more simple (i.e. too much happens in each), whether I should rather return value instead of alter through references passed, whether my general style is inelegant. I''m also concerned about my class structures. But I''ll show you _some_ code and let you decide. Please note that I''m not asking you to go through the actual logic of all this code, but rather just calls to functions, classes and the like. Davin, I chose not to comment on any particulars of your code. It is partly due to my laziness, and due to the fact that there are too many things I’’d change. The first couple headers produced at least a dozen lines I’’d type and that suggested to me that you have still ways to go and perhaps your venture into "better style" by asking advice is a tad premature. Start by getting and reading good books. "Effective C++" series, "C++ FAQ", and just plain "Accelerated C++" and even "TC++PL" are good sources of information about style if you know what to look for. You’’re asking good questions, try finding answers to them in those books first. Hell, simply read through the C++ FAQ Lite (you can find it here: http://www.parashift.com/c++-faq-lite/). Try not to believe everything you read (my reply to your post is no exception). However, if more than one person tells you to do something, try to at least make a note of that. Do write more code. Only by writing you will develop your style. And don’’t let anybody tell you what style to have, whatever you will develop should be good enough, but it doesn’’t have to be a copy of anything. Good luck! Victor P.S. Couldn’’t resist. Just a couple of pointers to improve your code dramatically and immediately. (a) Use initialisation of class members, not assignment in constructors. (b) Use at least some amount of indentation in your code, making it easier to understand. (c) If an arithmetic expression tends to span more than one line of code, wrap it into a function. (d) If there is even a shadow of a possibility that your lines are too long and are going to be wrapped [by a news reading software at posting], use /**/ comments instead of //; don’’t let your lines to have more length than an average screen can hold. (e) Use better news posting software, Google is notoriously bad. (f) Put your declarations in headers, definitions in source modules, your cpp files should never have to contain ’’extern’’ unless it’’s ’’extern "C"’’... What else is noticeable at the first glance? "using" directives in a namespace scope are dangerous... "friend" declarations can be omitted by better design... There is no need for "return" at the end of a void function... Conditions of "blah == true" are too verbose, they should be "blah"... Nah, I better stop now or I will never stop :-) Davin, I chose not to comment on any particulars of your code. It ispartly due to my laziness, and due to the fact that there are toomany things I''d change. The first couple headers produced atleast a dozen lines I''d type and that suggested to me that youhave still ways to go and perhaps your venture into "better style"by asking advice is a tad premature. Start by getting and reading good books. "Effective C++" series,"C++ FAQ", and just plain "Accelerated C++" and even "TC++PL" aregood sources of information about style if you know what to lookfor. You''re asking good questions, try finding answers to themin those books first. Hell, simply read through the C++ FAQ Lite(you can find it here: http://www.parashift.com/c++-faq-lite/). Try not to believe everything you read (my reply to your post isno exception). However, if more than one person tells you to dosomething, try to at least make a note of that. Do write more code. Only by writing you will develop your style.And don''t let anybody tell you what style to have, whatever youwill develop should be good enough, but it doesn''t have to bea copy of anything. Good luck! Victor P.S. Couldn''t resist. Just a couple of pointers to improve yourcode dramatically and immediately. (a) Use initialisation ofclass members, not assignment in constructors. (b) Use at leastsome amount of indentation in your code, making it easier tounderstand. (c) If an arithmetic expression tends to span morethan one line of code, wrap it into a function. (d) If there iseven a shadow of a possibility that your lines are too long andare going to be wrapped [by a news reading software at posting],use /**/ comments instead of //; don''t let your lines to have morelength than an average screen can hold. (e) Use better news postingsoftware, Google is notoriously bad. (f) Put your declarationsin headers, definitions in source modules, your cpp files shouldnever have to contain ''extern'' unless it''s ''extern "C"''... Whatelse is noticeable at the first glance? "using" directives ina namespace scope are dangerous... "friend" declarations can beomitted by better design... There is no need for "return" atthe end of a void function... Conditions of "blah == true" aretoo verbose, they should be "blah"... Nah, I better stop now orI will never stop :-) "wired" <lu*****@hotmail.com> wrote in message news:a5**************************@posting.google.c om..."wired" <lu*****@hotmail.com> wrote in messagenews:a5**************************@posting.google.c om... Hi, [snip] Victor’’s given you some good advice (with his usual brusque manner), here’’s some comment on the actual code. Although as Victor said, don’’t believe everything you read. Please note that I’’m not asking you to go through the actual logic of all this code, but rather just calls to functions, classes and the like. First point is the total absence of the keyword const. There are a zilllion places you should add this, obviously in your self teaching you didn’’t come across this concept, you should make it the very next thing you learn about. Thanks. Davin. //--------------------- //Globals.cpp - just some global variables needed by most functions #include <vector> #include "GLFiles.h" #include "Derived.h" using std::vector; //quadratic to draw cylinders, spheres etc. GLUquadricObj *quadratic; I’’m always doubtful about global variables. //window dimensions int WIDTH = 1024; const int WIDTH = 1024; Not going to point out every variable that you should be declaring const. Although global variables are dubious, global constants are fine. Also if you make a variable const, you should move it into a header file. int HEIGHT = 768; //table dimensions float length = 25.15f; // 251.5cm float width = 13.84f; float height = 8.13f; float goal = 4.0f; float rest = 1.5f; // width between esdge and tabletop // Misc int step = 150; // number of steps the collision detection will deconstruct each translation into in checking for a collision Mallet* Mallet1; Mallet* Mallet2; vector <Puck*> Pucks; More dubious globals, and I don’’t like pointers in STL classes, because it makes memory management awkward. Since Puck is a polymorphic class I would have used a smart pointer here. You can read about smart pointers in a good book, ’’More Effective C++’’ by Scott Meyers for instance. double friction = 0.985; int mouse_sens = 4; // 4-10, 10-least sensitive //--------------------- //Abstract.h - two abstract classes, some derived classes will be inherited from both #ifndef Abstract_Classes #define Abstract_Classes #include "Globals.h" using namespace std; class Movable; class Drawable; void AddDraw(Drawable* draw); void AddMove(Movable* move); extern double friction; class Drawable { public: Drawable() { AddDraw(this); } virtual void Draw() = 0; As a classes which is designed for derivation Drawable must have a virtual destructor other wise ou get undefined behaviour when you try to delete an object of a class derived from Drawable using a pointer to Drawable. virtual ~Drawable() {} }; class Movable { public: Movable(double x1, double y1, double r, Controller cont) { Not sure what Controller is but consider a const reference here for efficiency. And definitely prefer initialiation to assignment Movable(double x1, double y1, double r, const Controller& cont) : control(cont), x(x1), y(y1), radius(r), x_change(0.0), y_change(0.0) { AddMove(this); control = cont; x = x1; y = y1; x_change = 0.0; y_change = 0.0; radius = r; } Ditto, virtual destructor needed. virtual ~Movable() {} double next_x() { return x + friction * x_change; } Should be const as should most of the other methods. double next_x() const { return x + friction * x_change; } double next_y() { return y + friction * y_change; } Hi, [snip] Victor''s given you some good advice (with his usual brusque manner), here''ssome comment on the actual code. Although as Victor said, don''t believeeverything you read. Please note that I''m not asking you to go through the actual logic of all this code, but rather just calls to functions, classes and the like.First point is the total absence of the keyword const. There are a zilllionplaces you should add this, obviously in your self teaching you didn''t comeacross this concept, you should make it the very next thing you learn about. Thanks. Davin. //--------------------- //Globals.cpp - just some global variables needed by most functions #include <vector> #include "GLFiles.h" #include "Derived.h" using std::vector; //quadratic to draw cylinders, spheres etc. GLUquadricObj *quadratic;I''m always doubtful about global variables. //window dimensions int WIDTH = 1024;const int WIDTH = 1024; Not going to point out every variable that you should be declaring const.Although global variables are dubious, global constants are fine. Also ifyou make a variable const, you should move it into a header file. int HEIGHT = 768; //table dimensions float length = 25.15f; // 251.5cm float width = 13.84f; float height = 8.13f; float goal = 4.0f; float rest = 1.5f; // width between esdge and tabletop // Misc int step = 150; // number of steps the collision detection will deconstruct each translation into in checking for a collision Mallet* Mallet1; Mallet* Mallet2; vector <Puck*> Pucks;More dubious globals, and I don''t like pointers in STL classes, because itmakes memory management awkward. Since Puck is a polymorphic class I wouldhave used a smart pointer here. You can read about smart pointers in a goodbook, ''More Effective C++'' by Scott Meyers for instance. double friction = 0.985; int mouse_sens = 4; // 4-10, 10-least sensitive //--------------------- //Abstract.h - two abstract classes, some derived classes will be inherited from both #ifndef Abstract_Classes #define Abstract_Classes #include "Globals.h" using namespace std; class Movable; class Drawable; void AddDraw(Drawable* draw); void AddMove(Movable* move); extern double friction; class Drawable { public: Drawable() { AddDraw(this); } virtual void Draw() = 0;As a classes which is designed for derivation Drawable must have a virtualdestructor other wise ou get undefined behaviour when you try to delete anobject of a class derived from Drawable using a pointer to Drawable. virtual ~Drawable() {} }; class Movable { public: Movable(double x1, double y1, double r, Controller cont) {Not sure what Controller is but consider a const reference here forefficiency. And definitely prefer initialiation to assignment Movable(double x1, double y1, double r, const Controller& cont) :control(cont), x(x1), y(y1), radius(r), x_change(0.0), y_change(0.0) { AddMove(this); control = cont; x = x1; y = y1; x_change = 0.0; y_change = 0.0; radius = r; }Ditto, virtual destructor needed. virtual ~Movable() {} double next_x() { return x + friction * x_change; }Should be const as should most of the other methods. double next_x() const { return x + friction * x_change; } double next_y() { return y + friction * y_change; } [snip] OK, I’’m bored to now. You do look like a self taught programmer. You’’ve got some concepts spot on, and others you’’ve completely missed out on. You need a good book, and practise. john [snip] OK, I''m bored to now. You do look like a self taught programmer. You''ve gotsome concepts spot on, and others you''ve completely missed out on. You needa good book, and practise. johnwired wrote:wired wrote: It seems to me sometimes that I shouldn’’t have many void functions accepting addresses and changing their values for example but, rather, a double function that returns the calculated value. i.e. \tvoid Modify(Type& variable); should be replaced with \tType Transform(Type variable); or Type Transform(const Type& variable); I just don’’t know what’’s better code (in terms of speed, style, etc.). Sometimes, you must modify objects "in-place" so you would implement Type& Modify(Type& variable); where the function modifies variable in-place then returns a reference to variable so that your Modify function can be used in expressions that may also modify variable. For example, an object which is an instance of one of the container classes is almost always a variable which requires modification. Otherwise, whether you pass function arguments by value or by const reference usually depends only upon the size of the object. If the object occupies no more than one or two machine words, it is probably faster and more efficient to pass by value. Larger objects are almost always passed by const reference. cat f.h #include<iostream> class X { private: // representation int I; public: // operators operator int(void) const { return I; } X(int i): I(i) { } }; X f(X); X f(const X&); cat f.cc #include<f.h> X f(X x) { std::cout << "X f(X x)" << std::endl; return (int)x + 33; } X f(const X& x) { std::cout << "X f(const X& x)" << std::endl; return (int)x + 33; } cat main.cc #include<f.h> int main(int argc, char* argv[]) { X x = 22; X y = f(x); std::cout << (int)y << " = f(x)" << std::endl; return 0; } g++ -Wall -ansi -pedantic -I. -O2 -o main main.cc f.cc It seems to me sometimes that I shouldn''t have many void functions accepting addresses and changing their values for example but, rather, a double function that returns the calculated value. i.e. void Modify(Type& variable); should be replaced with Type Transform(Type variable);or Type Transform(const Type& variable); I just don''t know what''s better code (in terms of speed, style, etc.).Sometimes, you must modify objects "in-place" so you would implement Type& Modify(Type& variable); where the function modifies variable in-place then returns a referenceto variable so that your Modify function can be used in expressionsthat may also modify variable. For example,an object which is an instance of one of the container classesis almost always a variable which requires modification. Otherwise, whether you pass function argumentsby value or by const reference usually dependsonly upon the size of the object.If the object occupies no more than one or two machine words,it is probably faster and more efficient to pass by value.Larger objects are almost always passed by const reference. cat f.h#include<iostream> class X {private:// representationint I;public:// operatorsoperator int(void) const { return I; }X(int i): I(i) { }}; X f(X);X f(const X&); cat f.cc#include<f.h> X f(X x) {std::cout << "X f(X x)" << std::endl;return (int)x + 33;} X f(const X& x) {std::cout << "X f(const X& x)" << std::endl;return (int)x + 33;} cat main.cc#include<f.h> int main(int argc, char* argv[]) {X x = 22;X y = f(x);std::cout << (int)y << " = f(x)" << std::endl;return 0;} g++ -Wall -ansi -pedantic -I. -O2 -o main main.cc f.cc main.cc: In function `int main(int, char**)’’: main.cc:5: call of overloaded `f(X&)’’ is ambiguous f.h:13: candidates are: X f(X) f.h:14: X f(const X&) The nice thing about this is that you can substitute function f(const X&) for function f(X) and vice versa without affecting the meaning of the application program just as long as function f doesn’’t modify any global variable that can be passed to it in its argument list. [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated.第一次海报:做到这一点! ] main.cc: In function `int main(int, char**)'':main.cc:5: call of overloaded `f(X&)'' is ambiguousf.h:13: candidates are: X f(X)f.h:14: X f(const X&) The nice thing about this is that you can substitutefunction f(const X&) for function f(X) and vice versawithout affecting the meaning of the application programjust as long as function f doesn''t modify any global variablethat can be passed to it in its argument list.[ See http://www.gotw.ca/resources/clcm.htm for info about ][ comp.lang.c++.moderated. First time posters: Do this! ] 这篇关于关于风格/优雅和功能大小的偏执,请平息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
10-27 15:06