抛物运动 小球 二维 OpenGL C++

1.触墙反弹的斜抛运动

关键在于斜抛运动公式在编程动画中的使用

因为动画本身是逐帧播放的,用微积分的思想,叠加过程是动画本身有的了,我们就需要在函数中写每次的变化量就行了。

那每次变化是,速度变化量随加速度和时间而变,然后位移变化量又随着速度和时间而变。

每次计算变化量的时间区间我们自己写大小,设为t

对于横向运动来说,水平速度不变,每个delta t 内,都相比之前多运动了 vx*t,所以在Move函数内写x = x +vx*t;(Move的循环调用是后面在OpenGL动画中自动实现的,对我们来说,动画调用的就是Move函数)

对于竖直运动来说,竖直方向的速度,在每个delta t 内,会改变a*t,所以竖直方向的速度,每次迭代是这样写的,vy = vy + a*t;

竖直方向的位移,每个delta t 内变化, vy*t, 你就写 y = y +vy*t; 这样y坐标与t呈二次关系,并且受加速度控制。

后面的if判断是为了让小球不出view视图框,我们能一直看到它的运动,只要让小球在触及边界时,速度方向调转就行了。

// DrawBall.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include<gl/glut.h>
#include<stdlib.h>
#include<math.h>
#include<stdio.h>
#include<iostream>
#include<time.h>
using namespace std;
double v = 0.3;//初速度大小
double vx = v;
double vy = 2.5* v;
double a = -0.2;//向下加速度
static double x, y;//圆心坐标
double t = 0.01;

void MoveBall(void)
{
    x = x + vx*t;
    vy = vy + a*t;
    y = y + vy*t;
    if (y <= -1.5)
        vy = -vy;
    if (x >= 1.8 || x <= -1.8)
        vx = -vx;
}

//画一次圆
void DrawFilledCircle(double x, double y, double radius, int sections)
{
    float alpha;
    glBegin(GL_TRIANGLE_FAN);
    glVertex2f(x, y);
    for (int count = 0; count <= sections; count++)
    {
        alpha = count * 2 * 3.1415926 / sections;//圆心角
        glVertex2f(x + radius*cos(alpha), y + radius*sin(alpha));
    }
    glEnd();
}

void init(void)//初始化
{
    x = -1.5;
    y = -1.5;
    glClearColor(1.0, 1.0, 1.0, 0.0);//清屏色
    glOrtho(-2.0, 2.0, -2.0, 2.0, 2.0, -2.0);//视见体

}


void display(void)//重绘屏幕
{
    glClear(GL_COLOR_BUFFER_BIT);//清除颜色缓存
    glColor3f(0.0, 0.0, 1.0);//当前颜色
    DrawFilledCircle(x, y, 0.2,100);//在这里写绘制函数

                            glutSwapBuffers();//动画显示
    //glFlush();//静态显示 赶紧显示
}
void myTime(int value)
{
    MoveBall();
    glutPostRedisplay();
    glutTimerFunc(1, myTime, 1);
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);//glut库与控制台初始化函数
                          glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);//显示模式 单双缓存 颜色模式 深度
    //glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowPosition(500, 200);//窗口的位置 窗口左上角距离屏幕左上角
    glutInitWindowSize(500, 500);
    glutCreateWindow(" BallMovement ");//窗口名字
    init();//调用自定义的初始化函数

    glutDisplayFunc(display);//注册显示回调函数
    glutTimerFunc(1, myTime, 1);
    glutMainLoop();//进入事件循环
    return 0;
}

2.用鼠标点击控制运动小球每次斜抛运动的开始

每当鼠标点击,小球开始运动。

注意这样几个函数和变量

一个是自定义的鼠标回调函数mouse()

现在的功能是,按下左键就按照已经设定的速度抛小球

一个是全局变量jump

一个是初始化速度的initVelocity()

// DrawBall.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include<gl/glut.h>
#include<stdlib.h>
#include<math.h>
#include<stdio.h>
#include<iostream>
#include<time.h>
using namespace std;
double v ;//初速度大小
double vx ;
double vy;
double a = -0.2;//向下加速度
static double x, y;//圆心坐标
double t = 0.01;
bool jump = false;

void initVelocity()
{
    v = 0.3;
    vx = v;
    vy = 2.5* v;
}

void MoveBall(void)
{
    x = x + vx*t;
    vy = vy + a*t;
    y = y + vy*t;
    if (y <= -1.5)
    {
        vx = 0.0;
        vy = 0.0;
        jump = false;
    }
    if (x >= 1.8 || x <= -1.8)
        vx = -vx;
}

void mouseDown(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON)
    {
        jump = true;
        initVelocity();
    }
}

//画一次圆
void DrawFilledCircle(double x, double y, double radius, int sections)
{
    float alpha;
    glBegin(GL_TRIANGLE_FAN);
    glVertex2f(x, y);
    for (int count = 0; count <= sections; count++)
    {
        alpha = count * 2 * 3.1415926 / sections;//圆心角
        glVertex2f(x + radius*cos(alpha), y + radius*sin(alpha));
    }
    glEnd();
}

void init(void)//初始化
{
    x = -1.5;
    y = -1.5;
    glClearColor(1.0, 1.0, 1.0, 0.0);//清屏色
    glOrtho(-2.0, 2.0, -2.0, 2.0, 2.0, -2.0);//视见体
    
}


void display(void)//重绘屏幕
{
    glClear(GL_COLOR_BUFFER_BIT);//清除颜色缓存
    glColor3f(0.0, 0.0, 1.0);//当前颜色
    DrawFilledCircle(x, y, 0.2, 100);//在这里写绘制函数

    glutSwapBuffers();//动画显示
                      //glFlush();//静态显示 赶紧显示
}
void myTime(int value)
{
    if(jump)
    MoveBall();
    glutPostRedisplay();
    glutTimerFunc(1, myTime, 1);
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);//glut库与控制台初始化函数
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);//显示模式 单双缓存 颜色模式 深度
                                                //glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowPosition(500, 200);//窗口的位置 窗口左上角距离屏幕左上角
    glutInitWindowSize(500, 500);
    glutCreateWindow(" BallMovement ");//窗口名字
    init();//调用自定义的初始化函数

    glutDisplayFunc(display);//注册显示回调函数
    glutTimerFunc(1, myTime, 1);

    glutMouseFunc(mouseDown);

    glutMainLoop();//进入事件循环
    return 0;
}

 

继续改进鼠标功能

左键计数,来实现对小球初速度的控制,右键发射小球。 

新增

全局变量v_count 用它来加到初始化时的初速度上

注意每次球落地后,把v_count归零

 

// DrawBall.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include<gl/glut.h>
#include<stdlib.h>
#include<math.h>
#include<stdio.h>
#include<iostream>
#include<time.h>
using namespace std;
int v_count = 0;
double v ;//初速度大小
double vx ;
double vy;
double a = -0.2;//向下加速度
static double x, y;//圆心坐标
double t = 0.01;
bool jump = false;

void initVelocity()
{
    v = 0.1+0.01*v_count;
    vx = v;
    vy = 2.5* v;
}

void MoveBall(void)
{
    x = x + vx*t;
    vy = vy + a*t;
    y = y + vy*t;
    if (y <= -1.5)
    {
        vx = 0.0;
        vy = 0.0;
        v_count = 0;
        jump = false;
    }
    if (x >= 1.8 || x <= -1.8)
        vx = -vx;
}

void mouseDown(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON)
        v_count++;

    if (button == GLUT_RIGHT_BUTTON)
    {
        jump = true;
        initVelocity();
    }
}

//画一次圆
void DrawFilledCircle(double x, double y, double radius, int sections)
{
    float alpha;
    glBegin(GL_TRIANGLE_FAN);
    glVertex2f(x, y);
    for (int count = 0; count <= sections; count++)
    {
        alpha = count * 2 * 3.1415926 / sections;//圆心角
        glVertex2f(x + radius*cos(alpha), y + radius*sin(alpha));
    }
    glEnd();
}

void init(void)//初始化
{
    x = -1.5;
    y = -1.5;
    glClearColor(1.0, 1.0, 1.0, 0.0);//清屏色
    glOrtho(-2.0, 2.0, -2.0, 2.0, 2.0, -2.0);//视见体
    
}


void display(void)//重绘屏幕
{
    glClear(GL_COLOR_BUFFER_BIT);//清除颜色缓存
    glColor3f(0.0, 0.0, 1.0);//当前颜色
    DrawFilledCircle(x, y, 0.2, 100);//在这里写绘制函数

    glutSwapBuffers();//动画显示
                      //glFlush();//静态显示 赶紧显示
}
void myTime(int value)
{
    if(jump)
    MoveBall();
    glutPostRedisplay();
    glutTimerFunc(1, myTime, 1);
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);//glut库与控制台初始化函数
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);//显示模式 单双缓存 颜色模式 深度
                                                //glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowPosition(500, 200);//窗口的位置 窗口左上角距离屏幕左上角
    glutInitWindowSize(500, 500);
    glutCreateWindow(" BallMovement ");//窗口名字
    init();//调用自定义的初始化函数

    glutDisplayFunc(display);//注册显示回调函数
    glutTimerFunc(1, myTime, 1);

    glutMouseFunc(mouseDown);

    glutMainLoop();//进入事件循环
    return 0;
}

 

 

 

3.用鼠标按键时长控制小球运动的初速度大小,从而调节小球斜抛运动的水平距离。

4.视口追踪直播小球的运动,这样为了一直看到小球,我们不让小球反弹,小球在世界坐标系内一直运动,我们让视口跟随它运动。

最终目的,是为了用OpenGL实现模拟跳一跳游戏。

 

posted @ 2018-04-29 10:03  皓琪  Views(1487)  Comments(0)    收藏  举报