wawe | Дата: Четверг, 11.10.2012, 20:17 | Сообщение # 1 |
Просто человек...
Группа: Администраторы
Сообщений: 50
Награды: 3
Статус: Offline
| В предыдущем уроке я вам рассказывал, как сделать обычное окно. Да, как создавать обычные окна все уже знают. По этому сейчас будет идти речь о настройке GL3D метода, камеры, объектов и все.
Давайте все по порядку, начнем сперва с GL3D.
Вот, посмотрите на этот код
Code public void GET_INIT_GL3D() { GL11.glMatrixMode(GL11.GL_PROJECTION); GL11.glLoadIdentity(); GLU.gluPerspective(65.0f, (float) (DISPLAY_WIDTH / DISPLAY_HEIGHT), 0.0001f, 1050); GL11.glMatrixMode(GL11.GL_MODELVIEW); GL11.glLoadIdentity(); GL11.glClearColor(0.4f, 0.4f, 0.4f, 1.0f); GL11.glShadeModel(GL11.GL_SMOOTH); GL11.glClearDepth(1.0f); GL11.glEnable(GL11.GL_DEPTH_TEST); GL11.glDepthFunc(GL11.GL_LEQUAL); GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST); GL11.glEnable(GL11.GL_TEXTURE_2D); }
Эта часть кода нужна для настройки матрицы с пропорцией размеров окна. Code GL11.glMatrixMode(GL11.GL_PROJECTION); GL11.glLoadIdentity(); GLU.gluPerspective(65.0f, (float) (DISPLAY_WIDTH / DISPLAY_HEIGHT), 0.0001f, 1050); GL11.glMatrixMode(GL11.GL_MODELVIEW); GL11.glLoadIdentity();
Ширина(800) делится на высоту(600) и в итоге получаем число 400. Если же записать туда другое число, окно будет неправильно себя вести, то есть, все что находится в окне.
Код GL11.glClearColor(COLOR_RED, COLOR_GREEN, COLOR_BLUE); служит для изменения фонового цвета окна. В моем случае фон будет серым.
Этот код сглаживает объекты GL11.glShadeModel(GL11.GL_SMOOTH); Код для очистки буфера глубины GL11.glClearDepth(1.0f); Остальная часть кода делает просто выполняет свою работу. GL11.glEnable(GL11.GL_TEXTURE_2D); включает текстуры.
Если вы просто скопируете код в свою программу, он работать не будет. Потому-что мы забыли про самое главное... Очистка экрана и буфера глубины в методе RENDER();
Вот метод Code public void GET_INIT_RENDER() { // Очистить экран и буфер глубины GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); GL11.glLoadIdentity(); }
Метод GET_INIT_RENDER(){} служит для рендеринга всего, что будет в игре.
Если вы читали тему "LWJGL Основы - Таймер" тогда вы уже знаете, что на разных компьютерах скорость игры будет разной. А знаете почему? Потому-что по умолчанию игра работает со скоростью FPS. Разные компьютеры с разной скоростью(от мощных до слабых) имеют разную скорость FPS. Например на слабом компьютере FPS будет - 20, а значит игра будет работать медленнее. На мощном компьютере FPS будет 90, а значить скорость игры будет настолько высокой, что человек работающий со слабым компьютером будет поражен. По скольку он не сможете уловить все объекты, которые перемещаются с большой скоростью.
Сейчас я вам покажу, как решить данную проблему. Чтобы игра работала с одинаковой скоростью на разных компьютерах(независимо от FPS) нам понадобится еще пару сотен строк кода, шучу =)
Code public static float dt = 0.0f; public static long lastFPS; public static long lastFrame; public static int fps;
//...
public static long GetTime() { return (Sys.getTime() * 1000) / Sys.getTimerResolution(); }
public void UpdateFPS() { if (GetTime() - lastFPS > 1000) { Display.setTitle(TITLE + " FPS: " + fps); fps = 0; lastFPS += 1000; } fps++; }
//...
public void GET_INIT_RENDER() { // Очистить экран и буфер глубины GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); GL11.glLoadIdentity(); UpdateFPS(); }
//...
public void GET_INIT_RUNNING() { float lastTime = 0.0f; float time = 0.0f; lastFPS = GetTime(); // цикл будет работать до тех пор, пока RUNNING не будет равен false; // окно не будет закрыто; пользователь не нажмет клавишу ESC. while (RUNNING && !Display.isCloseRequested() && !keys[Keyboard.KEY_ESCAPE]) { time = Sys.getTime(); dt = (time - lastTime) / 1000.0f; lastTime = time; GET_INIT_UPDATE(); // обновление окна Display.update(); } // asCrash == false это число 0. Выход в нормальном состоянии cleanUp(false); }
Выше вы увидели обновленный код. Теперь проблема будет решена.
Теперь давайте создадим камеру
Camera.java Code import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.GL11; import org.lwjgl.util.vector.Vector3f;
public class Camera {
private boolean moveForward = false; private boolean moveBackward = false; private boolean strafeLeft = false; private boolean strafeRight = false; private boolean onGround = false, jumping = false, falling = false; private final float gravity = 0.0100f;
public float movementSpeed = 8.0f; public float yBottom = 0; public float yMotion = 0;
public Main world;
static Vector3f vector = new Vector3f(); Vector3f rotation = new Vector3f(); Vector3f previous = new Vector3f();
public Camera(Main app) { world = app; }
public Camera() {
}
public static float getX() { return vector.x; }
public static float getY() { return vector.y; }
public static float getZ() { return vector.z; }
public void Update() { UpdatePreviousVector(); UpdateMotion(movementSpeed * Main.dt); Input(); collisions(-0.9f); Physics(); }
// позиуия камеры public void TranslateCamera() { GL11.glRotatef(rotation.x, 1.0f, 0.0f, 0.0f); GL11.glRotatef(rotation.y, 0.0f, 1.0f, 0.0f); GL11.glRotatef(rotation.z, 0.0f, 0.0f, 1.0f); GL11.glTranslatef(-vector.x, -vector.y - 2.4f, -vector.z); }
public void UpdatePreviousVector() { previous.x = vector.x; previous.y = vector.y; previous.z = vector.z; } // физика камеры public void Physics() { onGround = (vector.y == yBottom && !jumping && !falling); jumping = (yMotion > 0); falling = (yMotion < 0); vector.y += yMotion; if (vector.y > yBottom) { yMotion -= gravity; } if (vector.y <= yBottom) { vector.y = yBottom; yMotion = 0; } }
public void collisions(float offset) { if (!ontopOfCollidablePoly(offset)) { yBottom = 0; } for (Quad v : world.quad) { if (Math.abs(vector.x - v.centerX) < v.width / 2 && Math.abs(vector.z - v.centerZ) < v.length / 2) { if ((Math.abs(vector.y - v.yTop * 2) < v.height + (offset)) || vector.y > v.yBottom + v.height - 0.5f) { yBottom = v.y1 + v.yTop; } else if (v.yBottom > vector.y + 2.4f) {
} else { vector.x = previous.x; vector.z = previous.z; } } } }
private boolean ontopOfCollidablePoly(float offset) { for (Quad v : world.quad) { if (Math.abs(vector.x - v.centerX) < v.width / 2 && Math.abs(vector.z - v.centerZ) < v.length / 2) { return true; } } return false; } // управление public void Input() { if (Main.keys[Keyboard.KEY_W]) { moveForward = true; } else { moveForward = false; } if (Main.keys[Keyboard.KEY_S]) { moveBackward = true; } else { moveBackward = false; } if (Main.keys[Keyboard.KEY_A]) { strafeLeft = true; } else { strafeLeft = false; } if (Main.keys[Keyboard.KEY_D]) { strafeRight = true; } else { strafeRight = false; } if (Main.keys[Keyboard.KEY_SPACE] && onGround) { yMotion = 0.2f; } // если курсор исчез... if (Mouse.isGrabbed()) { // произойдет FIX камеры float mouseDX = Mouse.getDX() * 0.8f * 0.16f; float mouseDY = Mouse.getDY() * 0.8f * 0.16f; if (rotation.y + mouseDX >= 360) { rotation.y = rotation.y + mouseDX - 360; } else if (rotation.y + mouseDX < 0) { rotation.y = 360 - rotation.y + mouseDX; } else { rotation.y += mouseDX; } if (rotation.x - mouseDY >= -89 && rotation.x - mouseDY <= 89) { rotation.x += -mouseDY; } else if (rotation.x - mouseDY < -89) { rotation.x = -89; } else if (rotation.x - mouseDY > 89) { rotation.x = 89; } } }
public void UpdateMotion(float distance) { if (moveForward) { vector.x += distance * (float) Math.sin(rotation.y * Math.PI / 180); vector.z += -distance * (float) Math.cos(rotation.y * Math.PI / 180); } if (moveBackward) { vector.x -= distance * (float) Math.sin(rotation.y * Math.PI / 180); vector.z -= -distance * (float) Math.cos(rotation.y * Math.PI / 180); } if (strafeLeft) { vector.x += distance * (float) Math.sin((rotation.y - 90) * Math.PI / 180); vector.z += -distance * (float) Math.cos((rotation.y - 90) * Math.PI / 180); } if (strafeRight) { vector.x += distance * (float) Math.sin((rotation.y + 90) * Math.PI / 180); vector.z += -distance * (float) Math.cos((rotation.y + 90) * Math.PI / 180); } } } Скопируйте код себе. и еще код куба Code import org.lwjgl.opengl.GL11;
public class Quad {
float x1, y1, z1, x2, y2, z2, centerX, centerY, centerZ; float width, height, length; float yTop2; float yTop, yBottom; int num; float cX2, cY2, cZ2;
public Quad(float startX, float startY, float startZ, float endX, float endY, float endZ, int number, float gh, float hj, float yu) { x1 = startX; y1 = startY; z1 = startZ; x2 = endX; y2 = endY; z2 = endZ; width = x2 - x1; height = y2 - y1; length = z2 - z1; centerX = x1 + (width / 2); centerY = y2 + (height / 2); centerZ = z1 + (length / 2); yTop = Math.abs(y2 - y1); yBottom = y1 + yTop - height; num = number; cX2 = gh; cY2 = hj; cZ2 = yu; }
// рендер блока public void render() { GL11.glPushMatrix(); GL11.glColor3f(cX2, cY2, cZ2); GL11.glBegin(GL11.GL_QUADS); // Top GL11.glVertex3f(x1, y1, z1); GL11.glTexCoord2f(0, 0); GL11.glVertex3f(x1 + width, y1, z1); GL11.glTexCoord2f(0.9f, 0); GL11.glVertex3f(x1 + width, y1, z1 + length); GL11.glTexCoord2f(0.9f, 0.9f); GL11.glVertex3f(x1, y1, z1 + length); GL11.glTexCoord2f(0, 0.9f); // Bottom GL11.glVertex3f(x1, y1 + height, z1); GL11.glTexCoord2f(0, 0); GL11.glVertex3f(x1 + width, y1 + height, z1); GL11.glTexCoord2f(0.9f, 0); GL11.glVertex3f(x1 + width, y1 + height, z1 + length); GL11.glTexCoord2f(0.9f, 0.9f); GL11.glVertex3f(x1, y1 + height, z1 + length); GL11.glTexCoord2f(0, 0.9f); // Front GL11.glVertex3f(x1, y1, z1); GL11.glTexCoord2f(0, 0); GL11.glVertex3f(x1 + width, y1, z1); GL11.glTexCoord2f(0.9f, 0); GL11.glVertex3f(x1 + width, y1 + height, z1); GL11.glTexCoord2f(0.9f, 0.9f); GL11.glVertex3f(x1, y1 + height, z1); GL11.glTexCoord2f(0, 0.9f); // Back GL11.glVertex3f(x1, y1, z1 + length); GL11.glTexCoord2f(0, 0); GL11.glVertex3f(x1 + width, y1, z1 + length); GL11.glTexCoord2f(0.9f, 0); GL11.glVertex3f(x1 + width, y1 + height, z1 + length); GL11.glTexCoord2f(0.9f, 0.9f); GL11.glVertex3f(x1, y1 + height, z1 + length); GL11.glTexCoord2f(0, 0.9f); // Left side GL11.glVertex3f(x1, y1, z1); GL11.glTexCoord2f(0, 0); GL11.glVertex3f(x1, y1, z1 + length); GL11.glTexCoord2f(0.9f, 0); GL11.glVertex3f(x1, y1 + height, z1 + length); GL11.glTexCoord2f(0.9f, 0.9f); GL11.glVertex3f(x1, y1 + height, z1); GL11.glTexCoord2f(0, 0.9f); // Right side GL11.glVertex3f(x1 + width, y1, z1); GL11.glTexCoord2f(0, 0); GL11.glVertex3f(x1 + width, y1, z1 + length); GL11.glTexCoord2f(0.9f, 0); GL11.glVertex3f(x1 + width, y1 + height, z1 + length); GL11.glTexCoord2f(0.9f, 0.9f); GL11.glVertex3f(x1 + width, y1 + height, z1); GL11.glTexCoord2f(0, 0.9f); GL11.glEnd(); GL11.glPopMatrix(); } }
обновленный код главного класса Main.java Code import java.util.ArrayList; import java.util.List;
import org.lwjgl.LWJGLException; import org.lwjgl.Sys; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.DisplayMode; import org.lwjgl.opengl.GL11; import org.lwjgl.util.glu.GLU;
public class Main {
public static boolean[] keys = new boolean[256]; public static final int DISPLAY_WIDTH = 800; public static final int DISPLAY_HEIGHT = 600; public String TITLE = "GL3Dgame"; public boolean VSYNC_ENABLED = false; public boolean RUNNING = true; public static final int MAX_FPS = 100; public static float dt = 0.0f; public static long lastFPS; public static long lastFrame; public static int fps;
// создание камеры Camera camera = new Camera(this);
// блоки List<Quad> quad = new ArrayList<Quad>();
// конструктор главного класса Main. public Main() { // добавить один блок // quad.add(new Quad(startX, startY, startZ, endX, endY, endZ, textureNum, colorX, colorY, colorZ)); // что значит textureNum, вы узнаете в следующем уроке. quad.add(new Quad(4, 0.1f, 4, 6, 2.1f, 6, 1, 1, 1, 1)); }
public static long GetTime() { return (Sys.getTime() * 1000) / Sys.getTimerResolution(); }
public void UpdateFPS() { if (GetTime() - lastFPS > 1000) { Display.setTitle(TITLE + " FPS: " + fps); fps = 0; lastFPS += 1000; } fps++; }
// заготовка для настройки дисплея. public void GET_INIT_DISPLAY() { try { // настраиваем размеры окна, в моем случае это 800x600 Display.setDisplayMode(new DisplayMode(DISPLAY_WIDTH, DISPLAY_HEIGHT)); // меняем заголовок окна Display.setTitle(TITLE); // вертикальная синхронизация в этот момент отключена Display.setVSyncEnabled(VSYNC_ENABLED); // создаем окно Display.create(); // если появилась ошибка... } catch (LWJGLException e) { // выводим в консоль сообщение об ошибке System.err.println("Error: Display.create"); // здесь объяснять не буду. e.printStackTrace(); // asCrash == true это число 1, сообщаем всем, что причина очистки // из за ошибки cleanUp(true); }
// это настройка Opengl GET_INIT_GL3D(); // запускаем цикл GET_INIT_RUNNING(); }
// заготовка для настройки 3D OpenGL. public void GET_INIT_GL3D() { GL11.glMatrixMode(GL11.GL_PROJECTION); GL11.glLoadIdentity(); GLU.gluPerspective(65.0f, (float) (DISPLAY_WIDTH / DISPLAY_HEIGHT), 0.0001f, 1050); GL11.glMatrixMode(GL11.GL_MODELVIEW); GL11.glLoadIdentity(); GL11.glClearColor(0.4f, 0.4f, 0.4f, 1.0f); GL11.glShadeModel(GL11.GL_SMOOTH); GL11.glClearDepth(1.0f); GL11.glEnable(GL11.GL_DEPTH_TEST); GL11.glDepthFunc(GL11.GL_LEQUAL); GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST); GL11.glEnable(GL11.GL_TEXTURE_2D); }
public void GET_INIT_RENDER() { // Очистить экран и буфер глубины GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); GL11.glLoadIdentity(); // установка позиции камеры camera.TranslateCamera(); // рендеринг всех блоков for (Quad q : quad) { q.render(); } }
// здесь мы поместим все в один метод, чтобы было легче работать. public void GET_INIT_UPDATE() { // установка камеры camera.Update(); // скрытие курсора Mouse.setGrabbed(true); mapKeys(); GET_INIT_RENDER(); UpdateFPS(); }
// ну а здесь будет главный цикл игры. public void GET_INIT_RUNNING() { float lastTime = 0.0f; float time = 0.0f; lastFPS = GetTime(); // цикл будет работать до тех пор, пока RUNNING не будет равен false; // окно не будет закрыто; пользователь не нажмет клавишу ESC. while (RUNNING && !Display.isCloseRequested() && !keys[Keyboard.KEY_ESCAPE]) { time = Sys.getTime(); dt = (time - lastTime) / 1000.0f; lastTime = time; GET_INIT_UPDATE(); // обновление окна Display.update(); Display.sync(MAX_FPS); } // asCrash == false это число 0. Выход в нормальном состоянии cleanUp(false); }
// массив для хранения Keyboard public void mapKeys() { for (int i = 0; i < keys.length; i++) { keys[i] = Keyboard.isKeyDown(i); } }
// очистка и разрушение окна public void cleanUp(boolean asCrash) { Display.destroy(); System.exit(asCrash ? 1 : 0); }
// здесь и так все понятно. public static void main(String[] args) { Main main = new Main(); main.GET_INIT_DISPLAY(); } }
Как вы уже заметили, в классе Camera есть метод collision() все мечтали о том, чтобы камера не проходила сквозь объекты =) Только проблема, что этот код не работает полностью. В следующем уроке я покажу вам как нанести на куб текстуру
Сообщение отредактировал wawe - Пятница, 12.10.2012, 17:11 |
|
| |
kodo | Дата: Понедельник, 03.06.2013, 23:40 | Сообщение # 3 |
Рядовой
Группа: Пользователи
Сообщений: 7
Награды: 0
Репутация: 0
Статус: Offline
| Не прошло и пол года и я вернулся к своему проектику) И нашёл "баг" вот в этой строчке: GLU.gluPerspective(65.0f, (float) (DISPLAY_WIDTH / DISPLAY_HEIGHT), 0.0001f, 1050); Собственно ошибка в (float) (DISPLAY_WIDTH / DISPLAY_HEIGHT) - преобразование к флоту идёт после вычисления соотношения, а само соотношение считается для целочисленных, и получается целым, а потом преобразуется к дробному. Собственно сам баг хорошо видно если запрыгнуть на кубик и повертеть камерой, видно как не пропорционально он меняется, да ине кубик он при этом, а прямоугольник. Решить можно например записав так: GLU.gluPerspective(65.0f, (float)DISPLAY_WIDTH / DISPLAY_HEIGHT, 0.0001f, 1050); собственно перед делением одна из величин становится дробной и результатом деления уже будет дробное, а не целое.
|
|
| |