first, Brief introduction
Recently also learned the case of bumper cars, this case is on GitHub upload case: Download address: Case address, here is mainly to do a simple summary, first look at the effect of the car's rotation effect of the production and collision detection, there is a third person view into the first person view of the collection. First look at the effect diagram
Third Person view
First person View
Each time the collision to another car, or hit the border, will be re-adjust the front of the head has been facing, that is, our steering wheel will always appear in front of our vision, that is, we look at the position, the following will be introduced.
Here are a few of the core features. About the other classes in this case on the other side to mention not to do too much introduction, one is Scenemesh class, about this class is mainly through Aglkvertexattribarraybuffer management vertex data, send vertex data to the GPU, allocate vertex data memory, Draw vertex data. Actually do is some glgenbuffers,glbindbuffer ah, etc. simply to help us encapsulate such a number of operations, we can no longer write.
Another is the Scenemodel class, which is a model class, which is the parent class of the Scenecarmodel class and the Scenerinkmodel class, with properties Axisalignedboundingbox primarily used to calculate the maximum minimum bounds of the model. There is also a property mesh for managing vertex data, where the Axisalignedboundingbox attribute is available in both the car model and the site model. For example, the axisalignedboundingbox of a car model is used to calculate its radius in Scenecar's Init method. This is to calculate if there are no out-of-bounds.
About the Scenecarmodel class, this class is a model class for car, including the car's vertex data and the basic properties of the model, which can be used to draw car models.
For the Scenerinkmodel class, this class is the model class for the scene, including the basic properties of the scene's vertex data and bounds, which can be used to plot the scene.
Another class is the Scenecar class, which deals with the speed, position, yaw, radius, filter functions, collision handling of vehicles, collision treatment of vehicles and venues, and the modeling of automobiles.
Then to introduce the first of our business code, rendering methods are in an inherited from the Glkviewcontroller controller to complete, the controller code is mainly to listen to the button click on the event Ah, as well as the settings when the rendering and the context of the settings ah and so on. The following is a simple introduction to the method in the controller.
The setup function below is to configure the OpenGL ES context and to create four cars, as well as to set up a site where the car shown in Figure one is opened in a box. There is also to set the position of the eye and the eyes to see the position.
-(void) setUp {//1. New OpenGL ES Context Eaglcontext *mcontext = [[Eaglcontext alloc]initwithapi:keaglrenderingapiopengle
S2];
Glkview *view = (Glkview *) Self.view;
View.context = Mcontext;
View.drawablecolorformat = GLKViewDrawableColorFormatRGBA8888;
View.drawabledepthformat = GLKViewDrawableDepthFormat16;
[Eaglcontext SetCurrentContext:view.context];
Open depth Test glenable (gl_depth_test);
glenable (Gl_blend);
_cars = [[Nsmutablearray alloc]init];
Self.baseeffect = [[Glkbaseeffect alloc]init];
Set illumination self.baseeffect.light0.enabled = Gl_true;
Self.baseEffect.light0.ambientColor = Glkvector4make (0.6f, 0.6f, 0.6f, 1.0f);
Self.baseEffect.light0.position = Glkvector4make (1.0f, 0.8f, 0.4f, 0.0f);
The Self.carmodel = [[Scenecarmodel Alloc]init], which is used in the Init method of the following car, is passed into the calculation radius.
Self.rinkmodel = [[Scenerinkmodel alloc]init];
Self.rinkboundingbox = Self.rinkModel.axisAlignedBoundingBox; //Create 4 cars//green car, A/* Parameter 1: Car model parameter 2: Car initial position parameter 3: Speed parameter 4: Color */Scenecar *newcar = [[Sceneca R Alloc] InitWithModel:self.carModel position:glkvector3make (1.0f, 0.0f, 1 .0f) Velocity:glkvector3make (1.5f, 0.0f, 1.5f) color:glkvector4make (0.0f,
0.5f, 0.0f, 1.0f)];
Newcar.mcarid = 1;
[_cars Addobject:newcar]; Red car, B newcar =[[scenecar alloc] InitWithModel:self.carModel position:glkvector3make (1.0f, 0.0f, -1.0f) velocity:glkvector3make ( -1.5f, 0.0f, -1.5f) color:glkvector4make (0.5f, 0.0f, 0.0f,
1.0f)];
Newcar.mcarid = 2;
[_cars Addobject:newcar]; Blue car, C newcar =[[scenecar alloc] InitWithModel:self.carModel position:glkvector3make (2.0f, 0.0f, -2.0f) velocity:glkvector3make ( -1.5f, 0.0f, -0.5f) color:glkvector4make (0.0f, 0.0f, 0.5f, 1.0f)];
Newcar.mcarid = 3;
[_cars Addobject:newcar]; Yellow car, D newcar =[[scenecar alloc] InitWithModel:self.carModel position:glkvector3make (5.0f, 0.0f, -5.0f) velocity:glkvector3make (2.0f, 0.0f, -1.0f) color:glkvector4make (1.0f, 1.0f, 0.0f,
1.0f)];
Newcar.mcarid = 4;
[_cars Addobject:newcar];
Eyeposition, which indicates the current eye position self.eyeposition = Glkvector3make (10.5f, 5.0f, 0.0f);
Set the position of the eye to see self.lookatposition = Glkvector3make (0.0f, 0.5f, 0.0f);
The targeteyeposition represents the location of the eye's final target//the need to set a target location for switching between views, through high-pass filter functions Scenevector3fastlowpassfilter and low-pass filter functions
Scenevector3slowlowpassfilter to achieve a smooth transition from perspective. */
}
Immediately following the update function, this function will be called automatically in Glkviewcontroller. Another method in Glkviewcontroller is that the Glkview:drawinrect method is called automatically, and then the call order is called first, it is automatically called, and then the call order is called first Glkview:drawinrect, Call the Update method again. In general, we have placed the scene data changes in the update, while the rendering code is placed in the Glkview.
-(void) Update {/* Eyeposition represents the location of the current eye, targeteyeposition represents the eye's ultimate purpose.
The location of a target needs to be set up for the purpose of switching from the perspective, through the high-pass filter function Scenevector3fastlowpassfilter and low-pass filter function Scenevector3slowlowpassfilter to achieve a smooth transition perspective. */* Below this Self.pointofviewanimationcountdown this can be understood to be used to do my view when the transition to a smooth effect of the experience, combined with the following Scenevector3slowlowpassfil ter to use */ if (Self.pointofviewanimationcountdown > 0) {//The following is done in order to switch the perspective of smooth conversion using Scenevector3slowlowpassfilte
R Self.pointofviewanimationcountdown-= self.timesincelastupdate;
Self.eyeposition = Scenevector3slowlowpassfilter (Self.timesincelastupdate,
Self.targeteyeposition, self.eyeposition); Self.lookatposition = Scenevector3slowlowpassfilter (self.timesinc
Elastupdate, Self.targetlookatposition, self.lookatposition); }//does not turn on toggle First Person View switch when it is called the following method else {//the current eye location Self.eyepOsition = Scenevector3fastlowpassfilter (Self.timesincelastupdate,
Self.targeteyeposition,
Self.eyeposition);
Look at the position self.lookatposition = Scenevector3fastlowpassfilter (
Self.timesincelastupdate, Self.targetlookatposition, self.lookatposition);
}//Car update car position, yaw angle and speed [_cars makeobjectsperformselector: @selector (Updatewithcontroller:) withobject:self];
[Self Updatepointofview] is also described below for this method; }
About Glkview: (Glkview *) View Drawinrect: (cgrect) The RECT function is mainly used for rendering the code, such as setting the screen Ah, set perspective projection, draw the model Ah, and then update the speed displayed on the label, and so on.
-(void) Glkview: (Glkview *) View Drawinrect: (cgrect) rect {glclearcolor (0.3f, 0.3f, 0.3f, 1.0f); Glclear (Gl_color_buffer_bit |
Gl_depth_buffer_bit);
Self.baseEffect.light0.diffuseColor = Glkvector4make (1.0f, 1.0f, 1.0f, 1.0f);
Set the projection matrix const glfloat Aspectratio = (glfloat) view.drawablewidth/(glfloat) view.drawableheight; Self.baseEffect.transform.projectionMatrix = Glkmatrix4makeperspective (Glkmathdegreestoradians (35.0f),
Aspectratio, 0.1f, 25.0f);
Set the model matrix/* GLKMatrix4 Glkmatrix4makelookat (float eyex, float eyey,
Float Eyez, float CenterX,
Float centery, float centerz, float UpX, Float upy, float upZ) returns a 4x4 matrix transform for the world coordinate camera Location of the machine, Target point locationand the up vector argument list Eyex The x-coordinate of the eye (Observer) position eyey the y-coordinate of the eye (Observer) position Eyez the z-coordinate of the eye (Observer) position CenterX the target point location, x centery Target point position, y Centerz target point position, z upX up vector x upy up vector y upZ up vector z *///Can be understood as setting observer position self.baseeffe
Ct.transform.modelviewMatrix = Glkmatrix4makelookat (self.eyeposition.x,
SELF.EYEPOSITION.Y, Self.eyeposition.z, Self.lookatposition.x, Self.lo
OKATPOSITION.Y, Self.lookatposition.z,
0, 1, 0);
Plot the scene [Self.baseeffect Preparetodraw];
[Self.rinkmodel draw]; Let every car in the array call the Drawwithbaseeffect method [_cars makeobjectsperformselector: @selector (drawwithbaseeffect:) withobject
: Self.baseeffect];
Number of collisions Self.myBounceLabel.text = [NSString stringwithformat:@ "%ld", [Scenecar Getbouncecount]];
Gets the last car in the array scenecar *viewcar = [_cars lastobject]; Update the label above to show the speed self.myvelocitylabel.text = [NSString stringwithformat:@ "%.1f", Glkvector3length (
Viewcar.velocity)]; }
Below to see this method, which is mainly in the waist switch to become the first person view when the need to do is to set the target eye position is the car above the 0.45 distance, and set the eye to look at the position, looking at the speed direction of the car. So here's what I can see when I switch to the first person.
-(void) Updatepointofview
{
if (!self.shouldusefirstpersonpov) {
self.targeteyeposition = Glkvector3make (10.5f, 5.0f, 0.0f);
Self.targetlookatposition = Glkvector3make (0.0f, 0.5f, 0.0f);
} else
{
Scenecar *viewcar = [_cars lastobject];
Self.targeteyeposition = Glkvector3make (viewcar.position.x, VIEWCAR.POSITION.Y + 0.45f, viewcar.position.z);
/*/
/If the viewcar.velocity directly to the edge of the border has not happened when the line of sight directly, including the collision with other bumper cars also although the speed direction has changed, but has not hit, has not collided with the line of
sight is crooked, here added _ Eyeposition can achieve a wobble after encountering the border, and then the line of sight has been on the steering wheel.
*
/self.targetlookatposition =glkvector3add (_eyeposition, viewcar.velocity);}
}
For the above Self.targetlookatposition =glkvector3add (_eyeposition, viewcar.velocity); If we change to self.targetlookatposition = viewcar.velocity; we're not at the border.
So what we're going to do is based on the position of the observer and then add it to the velocity vectors. It can be understood that the location of the direct two points is connected and then a direction is determined, and then my steering wheel can be rotated better.
And then there's the monitoring of some events.
Deceleration
-(ibaction) Onslow: (ID) Sender {
Scenecar *car = [_cars lastobject];
[Car onspeedchange:yes];
}
Acceleration
-(Ibaction) Onfast: (ID) Sender {
Scenecar *car = [_cars lastobject];
[Car onspeedchange:no];
}
Modify View
-(ibaction) Takeshouldusefirstpersonpovfrom: (Uiswitch *) Sender {
Self.shouldusefirstpersonpov = [ Sender IsOn];
POV (First person view-point of view) scene animation seconds
_pointofviewanimationcountdown = scenenumberofpovanimationseconds;
}
Let's go to the Scenecar class to see how to update the car's position, yaw angle, and speed.
-(void) Updatewithcontroller: (ID <SceneCarControllerProtocol>) Controller {//generates a time between 0.01-0.5 seconds Nstimeint
Erval elapsedtimeseconds = MIN (MAX ([controller timesincelastupdate], 0.01f), 0.5f);
Travel distance = speed * Time GLKVector3 traveldistance = Glkvector3multiplyscalar (self.velocity, elapsedtimeseconds);
Next point = Current position + Travel distance self.nextposition = Glkvector3add (self.position, traveldistance);
Get the scene box Sceneaxisallignedboundingbox Rinkboundingbox = [Controller Rinkboundingbox];
Detect collisions between car [self bounceoffcars:[controller cars] elapsedtime:elapsedtimeseconds];
Detects if the car touches the wall [self bounceoffwallswithboundingbox:rinkboundingbox]; if (Glkvector3length (self.velocity) < 0.1) {//speed is too small, the direction may be a dead corner.
So we're going to randomly generate a new direction glfloat randtemp = (random ()/(0.5f * rand_max))-1.0f;
self.velocity = Glkvector3make (Randtemp, 0.0f, randtemp); }else if (Glkvector3length (self.velociTY) < 4) {//Speed too slow, adjust speed self.velocity = Glkvector3multiplyscalar (self.velocity, 1.01f); }//Here According to my understanding is to calculate the angle between the two, and then go to that corresponding angle of float dotProduct = glkvector3dotproduct (Glkvector3normalize (self.velocit
Y), Glkvector3make (0.0f, 0.0f, -1.0f)); if (self.velocity.x >= 0.0f) {//Set yaw to negative actually is to set the angle of rotation in my model view matrix Self.targetyawradians =-ACOSF (d
OTPRODUCT);
}else {//Set yaw angle to positive Self.targetyawradians = ACOSF (dotProduct); }//Slowly rotate the direction of the operation, not the yaw angle of the low-pass filter processing, will be immediately converted angle, there will be no I from one angle smooth to another angle [self spintowarddirectionofmotion:elapsedtime
Seconds];
Update Self.position's Location self.position = self.nextposition; }
Here again why when self.velocity.x>0.0 when the angle of the negative, and then less than 0.0 when the positive, first we consider a situation, the speed of the reverse only the Z-direction component of the case, the other can also be used once
And look at the method of calculating the collision between the two cars.
-(void) Bounceoffcars: (Nsarray *) Cars ElapsedTime: (nstimeinterval) elapsedtimeseconds {for (Scenecar *currentcar in C
ARS) {if (Currentcar! = self) {//Distance glkvector3distance (A, B), gets the distance between the vector ab
float distance = glkvector3distance (self.nextposition, currentcar.nextposition);
Distance is less than the border diameter distance, it can be understood that 2 graphics are already overlapping, that is, the collision State//car collided with if (Distance < Self.radius * 2.0f) {
Number of collisions +1 bouncecount++;
Car A's speed GLKVector3 ownvelocity = self.velocity;
Car B speed GLKVector3 otherveloctiy = currentcar.velocity; B.position-a.position = The route of the collision may be sent straight GLKVector3 Directiontoothercar = Glkvector3subtract (currentcar.p
Osition, self.position); Normalize the Directiontoothercar direCtiontoothercar = Glkvector3normalize (Directiontoothercar); Directiontoothercar negative direction vector//Get collision route opposite negative direction vector GLKVector3 Negdirectiontoothercar = glkvect
Or3negate (Directiontoothercar); The calculation is ownvelocity*cosθ, in fact, can be understood as speed in the direction of Negdirectiontoothercar speed of the component glfloat A_dotproduct = Glkvector3do
Tproduct (ownvelocity, Negdirectiontoothercar); Get is a vector that is the product of a scalar and a vector GLKVector3 tanownvelocity = glkvector3multiplyscalar (negdirectiontoothercar,a_dotprod
UCT);
Still the same way glfloat b_dotproduct = Glkvector3dotproduct (otherveloctiy, Directiontoothercar); GLKVector3 tanothervelocity = Glkvector3multiplyscalar (directiontoothercar,b_dotproduc
T);
Distance GLKVector3 traveldistance;
Update a car crash after speed ownvelocity-tanownvelocity self.velocity = glkvector3subtract (ownveloci
Ty, tanownvelocity);
Calculate a car travel distance = speed * Time Traveldistance = Glkvector3multiplyscalar (self.velocity, elapsedtimeseconds); Update a car next position = Current position + Distance self.nextposition = Glkvector3add (self.position, Trave
Ldistance); Update car B after collision speed otherveloctiy-tanownvelocity currentcar.velocity = Glkvector3subtract (otherveloctiy, tanOt
hervelocity);
Calculate b car travel distance = speed * Time Traveldistance = Glkvector3multiplyscalar (currentcar.velocity, elapsedtimeseconds); Update B car Next position = Current position + Distance currentcar.nextposition = Glkvector3add (CURRENTCA
R.position, traveldistance); }
}
}
I understand how this speed is changing.
A method to check the collision of vehicle and site boundary
-(void) Bounceoffwallswithboundingbox: (Sceneaxisallignedboundingbox) Rinkboundingbox {/* through Rinkboundingbox, you can get the maximum, The minimum boundary. Based on the radius property of car, the radius is obtained by comparing the radius radius + nextposition with the maximum and minimum boundary of the Rinkboundingbox to determine whether the boundary is reached, and if the boundary is reached, the velocity vector of the corresponding axis is reversed.
*/if ((rinkboundingbox.min.x + Self.radius) > self.nextposition.x) {//Next x's position exceeds the minimum bounds
Self.nextposition = Glkvector3make (rinkboundingbox.min.b + Self.radius, SELF.NEXTPOSITION.Y, self.nextPosition.z);
After hitting the wall x direction, opposite self.velocity = Glkvector3make (-self.velocity.x, SELF.VELOCITY.Y, self.velocity.z); }else if ((Rinkboundingbox.max.x-self.radius) < self.nextposition.x) {//Next x's position exceeds the maximum bounds Self.nex
Tposition = Glkvector3make ((Rinkboundingbox.max.x-self.radius), SELF.NEXTPOSITION.Y, self.nextPosition.z);
After hitting the wall x direction, opposite self.velocity = Glkvector3make (-self.velocity.x, SELF.VELOCITY.Y, self.velocity.z); }//z bounds to determine if (RINKBOUNDINGBOX.MIN.Z + Self.radius) > self.nextposition.z) {self.nextposition = Glkvector3make (self.nextposition.x, SEL
F.NEXTPOSITION.Y, rinkboundingbox.min.z + Self.radius);
self.velocity = Glkvector3make (self.velocity.x, SELF.VELOCITY.Y,-self.velocity.z); }else if ((Rinkboundingbox.max.z-self.radius) < self.nextposition.z) {self.nextposition = Glkvector3make (s
Elf.nextposition.x, SELF.NEXTPOSITION.Y, (Rinkboundingbox.max.z-self.radius));
self.velocity = Glkvector3make (self.velocity.x, SELF.VELOCITY.Y,-self.velocity.z); }
}
Let's look at the drawing method below.
-(void) Drawwithbaseeffect: (Glkbaseeffect *) Aneffect {//Model view matrix GLKMatrix4 Savedmodelviewmatrix = ANEFFECT.TRANSFO
Rm.modelviewmatrix; Sets the diffuse color.
Save up first GLKVector4 Saveddiffusecolor = AnEffect.material.diffuseColor;
Set Ambient color GLKVector4 savedambientcolor = AnEffect.material.ambientColor; Convert to Model View AnEffect.transform.modelviewMatrix = Glkmatrix4translate (Savedmodelviewmatrix,_position.x, _POSITION.Y, _
POSITION.Z); Rotational yaw size around y axis AnEffect.transform.modelviewMatrix = glkmatrix4rotate (AnEffect.transform.modelviewMatrix,
Self.yawradians, 0.0f, 1.0f, 0.0f);
Set color AnEffect.material.diffuseColor = Self.color;
AnEffect.material.ambientColor = Self.color;
[Aneffect Preparetodraw];
[_model draw];
After drawing, the matrix is restored, the color AnEffect.transform.modelviewMatrix = Savedmodelviewmatrix;
AnEffect.material.diffuseColor = Saveddiffusecolor;
AnEffect.material.ambientColor = Savedambientcolor; }
A function that is too slow or too fast to call
-(void) Onspeedchange: (BOOL) Slow
{
if (slow) {
self.velocity = glkvector3multiplyscalar (self.velocity, 0.9);
} else
{
self.velocity = glkvector3multiplyscalar (self.velocity, 1.1);
}
}
The functions related to yaw calculation are mainly for the smooth switching of angles and the following high-pass filter functions, 50.0 is a large replaceable constant. Can simulate the effect of shock after hitting the wall.
-(void) Spintowarddirectionofmotion: (nstimeinterval) Elapsed {//Yaw angle Self.yawradians = Scenescalarslowlowpassfilter
(Elapsed, Self.targetyawradians, Self.yawradians);
if (Self.mcarid > 0) {NSLog (@ "Yawradius%f", Glkmathradianstodegrees (Self.yawradians)); }} #pragma mark-spinmethod//50.0, can be changed. The shock effect after the model hits the wall.
Since the 50.0 is larger, the current value can be increased beyond target. Glfloat scenescalarfastlowpassfilter (nstimeinterval timesincelastupdate,glfloat target,GLfloat current) {// Yawradius + (50.0 * time * (Targetyawradians-yawradius)) return current + (50.0 * timesincelastupdate * (Target-curr
ENT)); }//4.0, it can be changed. You can simulate the effect of the view switching process.
Because 4.0 is smaller, current will gradually approach target. Glfloat Scenescalarslowlowpassfilter (Nstimeinterval timesincelastupdate, glfloat target,GLfloat current) {//YawRadiu
S + (4.0 * time * (Targetyawradians-yawradius)) return current + (4.0 * timesincelastupdate * (target-current)); }//High pass filter function-for the view switch can be smoothly switched GLKVector3 Scenevector3fastlowpassfilter (NStimeinterval timesincelastupdate, GLKVector3 Target,glkvector3 current) {return Glkvector3make ( Scenescalarfastlowpassfilter (Timesincelastupdate, Target.x, current.x), Scenescalarfas Tlowpassfilter (Timesincelastupdate, Target.y, Current.y), Scenescalarfastlowpassfilter (timeSince
Lastupdate, Target.z, current.z)); }//Low-pass filter function-Smooth switching GLKVector3 scenevector3slowlowpassfilter (nstimeinterval Timesincelastupdate,glkvector3 for viewing angle switching) Target,glkvector3 current) {return Glkvector3make (Scenescalarslowlowpassfilter (Timesincela Stupdate, Target.x, Current.x), Scenescalarslowlowpassfilter (Timesincelastupdate, Target.y, Curr
ENT.Y), Scenescalarslowlowpassfilter (Timesincelastupdate, Target.z, current.z));
}