Java项目实战--使用Java和LWJGL构建Minecraft风格游戏

引言

Minecraft是一款风靡全球的沙盒游戏,其简约的体素风格和开放世界的游戏性深受玩家喜爱。本文将介绍如何使用Java和LWJGL(Lightweight Java Game Library)构建一个简化版的Minecraft风格游戏,包括基本的地形生成、方块渲染以及玩家控制等功能。

技术栈

  • Java:核心编程语言
  • LWJGL:Java游戏开发库,提供对OpenGL的访问
  • JOML:Java OpenGL数学库,用于3D数学计算
  • STB Image:图像加载库
  • Maven:项目管理和依赖处理

游戏截图

在这里插入图片描述

项目结构

项目采用模块化设计,主要包含以下几个部分:

MinecraftDemo/
├── src/main/java/com/minecraft/
│   ├── MinecraftDemo.java       # 主类
│   ├── entity/                  # 实体相关类
│   │   ├── Entity.java          # 实体基类
│   │   └── Player.java          # 玩家类
│   ├── render/                  # 渲染相关类
│   │   ├── Camera.java          # 相机类
│   │   ├── Renderer.java        # 渲染器
│   │   ├── Shader.java          # 着色器管理
│   │   └── Texture.java         # 纹理管理
│   └── world/                   # 世界相关类
│       ├── Block.java           # 方块类
│       ├── Chunk.java           # 区块类
│       └── World.java           # 世界类
└── pom.xml                      # Maven配置文件

核心概念

1. 方块系统

在Minecraft风格游戏中,世界由无数个立方体方块组成。每个方块都有自己的类型(如泥土、石头、草地等)和对应的视觉表现。

public class Block {
    // 方块类型枚举
    public enum BlockType {
        AIR(new Vector3f(0.0f, 0.0f, 0.0f)),
        GRASS(new Vector3f(0.2f, 0.8f, 0.2f)),
        DIRT(new Vector3f(0.6f, 0.3f, 0.0f)),
        STONE(new Vector3f(0.5f, 0.5f, 0.5f)),
        WATER(new Vector3f(0.0f, 0.0f, 0.8f)),
        WOOD(new Vector3f(0.3f, 0.2f, 0.0f)),
        LEAVES(new Vector3f(0.0f, 0.5f, 0.0f));
        
        private final Vector3f color;
        
        BlockType(Vector3f color) {
            this.color = color;
        }
        
        public Vector3f getColor() {
            return color;
        }
    }
    
    private BlockType type;
    private float x, y, z;
    
    // 构造函数、getter和setter方法...
}

2. 区块系统

为了提高性能,世界被分割成多个固定大小的区块(Chunk)。只有玩家附近的区块才会被加载和渲染。

public class Chunk {
    // 区块大小常量
    public static final int CHUNK_SIZE = 16;
    public static final int CHUNK_HEIGHT = 128;
    
    // 区块坐标
    private final int chunkX;
    private final int chunkZ;
    
    // 存储区块中的所有方块
    private final Block[][][] blocks;
    
    // 顶点数据
    private float[] vertices;
    private float[] colors;
    private int vertexCount;
    
    // 区块网格生成方法...
}

3. 世界生成

世界生成是Minecraft风格游戏的核心特性之一。我们使用简单的噪声函数来生成自然的地形高度图。

public void generateBaseTerrain() {
    // 遍历所有区块
    for (int chunkX = 0; chunkX < worldSizeX; chunkX++) {
        for (int chunkZ = 0; chunkZ < worldSizeZ; chunkZ++) {
            // 遍历区块内的所有方块
            for (int x = 0; x < Chunk.CHUNK_SIZE; x++) {
                for (int z = 0; z < Chunk.CHUNK_SIZE; z++) {
                    // 计算世界坐标
                    int worldX = chunkX * Chunk.CHUNK_SIZE + x;
                    int worldZ = chunkZ * Chunk.CHUNK_SIZE + z;
                    
                    // 使用简单噪声函数生成高度
                    int height = generateHeight(worldX, worldZ);
                    
                    // 限制高度
                    height = Math.min(height, 60);
                    
                    // 填充方块
                    for (int y = 0; y < height; y++) {
                        // 确定方块类型
                        Block.BlockType type;
                        if (y == height - 1) {
                            type = Block.BlockType.GRASS;
                        } else if (y > height - 4) {
                            type = Block.BlockType.DIRT;
                        } else {
                            type = Block.BlockType.STONE;
                        }
                        setBlockType(worldX, y, worldZ, type);
                    }
                }
            }
        }
    }
}

4. 渲染系统

渲染系统负责将3D世界呈现在屏幕上。我们使用OpenGL来实现高效的图形渲染。

public void render(World world, Camera camera) {
    // 使用着色器程序
    glUseProgram(shaderProgram);
    
    // 设置视图和投影矩阵
    Matrix4f viewMatrix = camera.getViewMatrix();
    Matrix4f projectionMatrix = camera.getProjectionMatrix();
    
    // 将矩阵传递给着色器
    viewMatrix.get(matrixBuffer);
    glUniformMatrix4fv(viewMatrixLocation, false, matrixBuffer);
    
    projectionMatrix.get(matrixBuffer);
    glUniformMatrix4fv(projectionMatrixLocation, false, matrixBuffer);
    
    // 绑定VAO
    glBindVertexArray(vao);
    
    // 渲染所有区块
    Chunk[][][] chunks = world.getChunks();
    for (int x = 0; x < world.getWorldSizeX(); x++) {
        for (int y = 0; y < world.getWorldSizeY(); y++) {
            for (int z = 0; z < world.getWorldSizeZ(); z++) {
                Chunk chunk = chunks[x][y][z];
                renderChunk(chunk);
            }
        }
    }
    
    // 解绑VAO和着色器程序
    glBindVertexArray(0);
    glUseProgram(0);
}

5. 相机系统

相机系统控制玩家在3D世界中的视角。我们实现了第一人称视角,允许玩家自由移动和旋转视角。

public class Camera {
    // 相机位置
    private Vector3f position;
    
    // 相机朝向(欧拉角)
    private float yaw;   // 水平旋转角度
    private float pitch; // 垂直旋转角度
    
    // 相机方向向量
    private Vector3f front;
    private Vector3f up;
    private Vector3f right;
    
    // 处理鼠标移动
    public void processMouseMovement(float xoffset, float yoffset) {
        float sensitivity = 0.1f;
        xoffset *= sensitivity;
        yoffset *= sensitivity;
        
        yaw += xoffset;
        pitch -= yoffset;
        
        // 限制俯仰角度,防止万向节锁
        if (pitch > 89.0f) {
            pitch = 89.0f;
        }
        if (pitch < -89.0f) {
            pitch = -89.0f;
        }
        
        // 更新相机方向向量
        updateCameraVectors();
    }
    
    // 计算视图矩阵
    public Matrix4f getViewMatrix() {
        Vector3f target = new Vector3f();
        position.add(front, target);
        
        viewMatrix.identity();
        viewMatrix.lookAt(position, target, up);
        
        return viewMatrix;
    }
}

性能优化

在开发过程中,我们遇到了一些性能问题,特别是内存不足(OutOfMemoryError)。以下是我们采取的优化措施:

  1. 减小区块高度:将CHUNK_HEIGHT从256减小到128
  2. 优化顶点数组:假设只有20%的方块是可见的,减少了顶点数组的大小
  3. 减小世界大小:从16x8x16减小到5x5x5
  4. 减少树木数量:从100减少到30
  5. 限制地形高度:将最大高度限制在60
  6. JVM内存参数:添加-Xmx512m -Xms256m参数

交互功能

为了提高用户体验,我们实现了以下交互功能:

  1. 鼠标捕获切换:使用Tab键可以释放或捕获鼠标,方便在游戏和其他应用之间切换
  2. 第一人称控制:使用WASD键移动,鼠标控制视角
  3. 退出功能:按ESC键退出游戏
// 设置按键回调
glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
    if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) {
        glfwSetWindowShouldClose(window, true);
    }
    
    // 按Tab键切换鼠标捕获状态
    if (key == GLFW_KEY_TAB && action == GLFW_RELEASE) {
        toggleMouseCapture();
    }
});

// 设置鼠标移动回调
glfwSetCursorPosCallback(window, (window, xpos, ypos) -> {
    if (mouseGrabbed) {
        float dx = (float) (xpos - lastMouseX);
        float dy = (float) (ypos - lastMouseY);
        camera.processMouseMovement(dx, dy);
    }
    lastMouseX = xpos;
    lastMouseY = ypos;
});

调试与问题解决

在开发过程中,我们遇到了一些常见问题及其解决方案:

  1. 渲染问题:只显示蓝色背景,没有方块

    • 解决方案:确保正确生成区块网格,调整玩家和相机位置
  2. PNG纹理警告libpng warning: iCCP: known incorrect sRGB profile

    • 解决方案:在Texture类中添加stbi_set_unpremultiply_on_load(false)stbi_convert_iphone_png_to_rgb(true)
  3. 鼠标控制问题:鼠标被限制在游戏窗口内

    • 解决方案:实现鼠标捕获切换功能

结论

通过本项目,我们成功实现了一个基本的Minecraft风格游戏,包括地形生成、方块渲染和玩家控制等核心功能。虽然这只是一个简化版本,但它展示了构建3D体素游戏的基本原理和技术。

未来可以考虑添加更多功能,如:

  • 更复杂的地形生成算法
  • 方块放置和破坏功能
  • 物理系统
  • 多人游戏支持
  • 更丰富的方块类型和生物群系

参考资源


源代码

Directory Content Summary

Source Directory: ./MinecraftDemo

Directory Structure

MinecraftDemo/
  dependency-reduced-pom.xml
  mvn
  pom.xml
  src/
    main/
      java/
        com/
          minecraft/
            MinecraftDemo.java
            entity/
              Entity.java
              Mob.java
              Player.java
            render/
              Camera.java
              Renderer.java
              Texture.java
            world/
              Block.java
              Chunk.java
              World.java

File Contents

dependency-reduced-pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.minecraft</groupId>
  <artifactId>MinecraftDemo</artifactId>
  <version>1.0-SNAPSHOT</version>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <source>${maven.compiler.source}</source>
          <target>${maven.compiler.target}</target>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>3.0.0</version>
        <configuration>
          <mainClass>com.minecraft.MinecraftDemo</mainClass>
          <arguments>
            <argument>-Xmx512m</argument>
            <argument>-Xms256m</argument>
          </arguments>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.4</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer>
                  <mainClass>com.minecraft.MinecraftDemo</mainClass>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.lwjgl</groupId>
        <artifactId>lwjgl-bom</artifactId>
        <version>${lwjgl.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <properties>
    <maven.compiler.target>11</maven.compiler.target>
    <maven.compiler.source>11</maven.compiler.source>
    <lwjgl.natives>natives-windows</lwjgl.natives>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <joml.version>1.10.5</joml.version>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <lwjgl.version>3.3.2</lwjgl.version>
  </properties>
</project>

mvn


pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.minecraft</groupId>
    <artifactId>MinecraftDemo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <lwjgl.version>3.3.2</lwjgl.version>
        <joml.version>1.10.5</joml.version>
        <lwjgl.natives>natives-windows</lwjgl.natives>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.lwjgl</groupId>
                <artifactId>lwjgl-bom</artifactId>
                <version>${lwjgl.version}</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.lwjgl</groupId>
            <artifactId>lwjgl</artifactId>
        </dependency>
        <dependency>
            <groupId>org.lwjgl</groupId>
            <artifactId>lwjgl-assimp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.lwjgl</groupId>
            <artifactId>lwjgl-glfw</artifactId>
        </dependency>
        <dependency>
            <groupId>org.lwjgl</groupId>
            <artifactId>lwjgl-openal</artifactId>
        </dependency>
        <dependency>
            <groupId>org.lwjgl</groupId>
            <artifactId>lwjgl-opengl</artifactId>
        </dependency>
        <dependency>
            <groupId>org.lwjgl</groupId>
            <artifactId>lwjgl-stb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.lwjgl</groupId>
            <artifactId>lwjgl</artifactId>
            <classifier>${lwjgl.natives}</classifier>
        </dependency>
        <dependency>
            <groupId>org.lwjgl</groupId>
            <artifactId>lwjgl-assimp</artifactId>
            <classifier>${lwjgl.natives}</classifier>
        </dependency>
        <dependency>
            <groupId>org.lwjgl</groupId>
            <artifactId>lwjgl-glfw</artifactId>
            <classifier>${lwjgl.natives}</classifier>
        </dependency>
        <dependency>
            <groupId>org.lwjgl</groupId>
            <artifactId>lwjgl-openal</artifactId>
            <classifier>${lwjgl.natives}</classifier>
        </dependency>
        <dependency>
            <groupId>org.lwjgl</groupId>
            <artifactId>lwjgl-opengl</artifactId>
            <classifier>${lwjgl.natives}</classifier>
        </dependency>
        <dependency>
            <groupId>org.lwjgl</groupId>
            <artifactId>lwjgl-stb</artifactId>
            <classifier>${lwjgl.natives}</classifier>
        </dependency>
        <dependency>
            <groupId>org.joml</groupId>
            <artifactId>joml</artifactId>
            <version>${joml.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <mainClass>com.minecraft.MinecraftDemo</mainClass>
                    <arguments>
                        <argument>-Xmx512m</argument>
                        <argument>-Xms256m</argument>
                    </arguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.minecraft.MinecraftDemo</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

src\main\java\com\minecraft\MinecraftDemo.java

package com.minecraft;

import com.minecraft.entity.Player;
import com.minecraft.render.Camera;
import com.minecraft.render.Renderer;
import com.minecraft.world.Chunk;
import com.minecraft.world.World;

import org.joml.Vector3f;
import org.lwjgl.Version;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import org.lwjgl.system.MemoryStack;

import java.nio.IntBuffer;

import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryStack.stackPush;
import static org.lwjgl.system.MemoryUtil.NULL;

/**
 * 我的世界Demo主类
 * 负责初始化游戏窗口、处理输入和游戏主循环
 * 
 * 注意:运行时请添加以下JVM参数以增加内存:
 * -Xmx512m -Xms256m
 */
public class MinecraftDemo {

    // 窗口宽度和高度
    private final int WIDTH = 1280;
    private final int HEIGHT = 720;

    // 窗口句柄
    private long window;
    
    // 游戏世界
    private World world;
    
    // 玩家
    private Player player;
    
    // 相机
    private Camera camera;
    
    // 渲染器
    private Renderer renderer;
    
    // 鼠标状态
    private boolean mouseGrabbed = true;
    private double lastMouseX = 0;
    private double lastMouseY = 0;
    
    // 鼠标捕获状态
    private boolean mouseCaptured = true;

    public static void main(String[] args) {
        new MinecraftDemo().run();
    }

    public void run() {
        System.out.println("启动LWJGL " + Version.getVersion());

        init();
        loop();

        // 释放窗口和窗口回调
        glfwFreeCallbacks(window);
        glfwDestroyWindow(window);

        // 终止GLFW并释放错误回调
        glfwTerminate();
        glfwSetErrorCallback(null).free();
    }

    private void init() {
        // 设置错误回调
        GLFWErrorCallback.createPrint(System.err).set();
        
        // 初始化GLFW
        if (!glfwInit()) {
            throw new IllegalStateException("Unable to initialize GLFW");
        }
        
        // 配置GLFW
        glfwDefaultWindowHints();
        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
        glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
        
        // 创建窗口
        window = glfwCreateWindow(WIDTH, HEIGHT, "Minecraft Demo", NULL, NULL);
        if (window == NULL) {
            throw new RuntimeException("Failed to create the GLFW window");
        }
        
        // 设置按键回调
        glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
            if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) {
                glfwSetWindowShouldClose(window, true);
            }
            
            // 按Tab键切换鼠标捕获状态
            if (key == GLFW_KEY_TAB && action == GLFW_RELEASE) {
                toggleMouseCapture();
            }
        });

        // 设置鼠标移动回调
        glfwSetCursorPosCallback(window, (window, xpos, ypos) -> {
            if (mouseGrabbed) {
                float dx = (float) (xpos - lastMouseX);
                float dy = (float) (ypos - lastMouseY);
                camera.processMouseMovement(dx, dy);
            }
            lastMouseX = xpos;
            lastMouseY = ypos;
        });

        // 获取线程栈并压入新帧
        try (MemoryStack stack = stackPush()) {
            IntBuffer pWidth = stack.mallocInt(1);
            IntBuffer pHeight = stack.mallocInt(1);

            // 获取窗口大小
            glfwGetWindowSize(window, pWidth, pHeight);

            // 获取主显示器的分辨率
            GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());

            // 将窗口居中
            glfwSetWindowPos(
                    window,
                    (vidmode.width() - pWidth.get(0)) / 2,
                    (vidmode.height() - pHeight.get(0)) / 2
            );
        }

        // 使OpenGL上下文成为当前
        glfwMakeContextCurrent(window);
        // 启用垂直同步
        glfwSwapInterval(1);

        // 显示窗口
        glfwShowWindow(window);
        
        // 初始化OpenGL
        GL.createCapabilities();
        
        // 设置视口
        glViewport(0, 0, WIDTH, HEIGHT);
        
        // 设置清除颜色
        glClearColor(0.5f, 0.7f, 1.0f, 1.0f);
        
        // 启用深度测试
        glEnable(GL_DEPTH_TEST);
        
        // 启用面剔除
        glEnable(GL_CULL_FACE);
        glCullFace(GL_BACK);
        
        // 创建世界
        world = new World(5, 5, 5);
        
        // 生成地形
        world.generateTerrain();
        
        // 创建玩家
        player = new Player(new Vector3f(8, 50, 8));
        
        // 创建相机
        camera = new Camera(new Vector3f(8, 50, 8));
        camera.setAspectRatio(WIDTH, HEIGHT);
        
        // 创建渲染器
        renderer = new Renderer(camera);
        
        // 强制生成所有区块的网格
        Chunk[][][] chunks = world.getChunks();
        for (int x = 0; x < world.getWorldSizeX(); x++) {
            for (int y = 0; y < world.getWorldSizeY(); y++) {
                for (int z = 0; z < world.getWorldSizeZ(); z++) {
                    chunks[x][y][z].generateMesh();
                }
            }
        }
        
        System.out.println("初始化完成!");
        System.out.println("LWJGL版本: " + Version.getVersion());
        System.out.println("OpenGL版本: " + glGetString(GL_VERSION));
        System.out.println("世界大小: " + world.getWorldSizeX() + "x" + world.getWorldSizeY() + "x" + world.getWorldSizeZ());
        System.out.println("玩家位置: " + player.getPosition().x + ", " + player.getPosition().y + ", " + player.getPosition().z);
        
        // 捕获鼠标
        toggleMouseCapture();
    }

    private void toggleMouseCapture() {
        mouseCaptured = !mouseCaptured;
        
        if (mouseCaptured) {
            // 捕获鼠标
            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
        } else {
            // 释放鼠标
            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
        }
    }

    private void loop() {
        // 帧率计算
        double lastTime = glfwGetTime();
        int frameCount = 0;
        
        // 上一帧时间
        float lastFrame = 0;

        // 游戏循环
        while (!glfwWindowShouldClose(window)) {
            // 计算帧时间
            float currentFrame = (float) glfwGetTime();
            float deltaTime = currentFrame - lastFrame;
            lastFrame = currentFrame;
            
            // 处理输入
            processInput(deltaTime);
            
            // 更新游戏逻辑
            update(deltaTime);
            
            // 渲染
            render();
            
            // 打印调试信息
            printDebugInfo();
            
            // 交换缓冲区并轮询事件
            glfwSwapBuffers(window);
            glfwPollEvents();
            
            // 计算帧率
            frameCount++;
            if (currentFrame - lastTime >= 1.0) {
                System.out.println("FPS: " + frameCount);
                frameCount = 0;
                lastTime = currentFrame;
            }
        }
    }
    
    private void processInput(float deltaTime) {
        // 处理WASD移动
        if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
            player.processKeyboard(Player.Movement.FORWARD, deltaTime);
        if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
            player.processKeyboard(Player.Movement.BACKWARD, deltaTime);
        if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
            player.processKeyboard(Player.Movement.LEFT, deltaTime);
        if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
            player.processKeyboard(Player.Movement.RIGHT, deltaTime);
        if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS)
            player.processKeyboard(Player.Movement.UP, deltaTime);
        if (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS)
            player.processKeyboard(Player.Movement.DOWN, deltaTime);
        
        // 鼠标控制视角(仅在鼠标被捕获时)
        if (mouseCaptured) {
            double[] xpos = new double[1];
            double[] ypos = new double[1];
            glfwGetCursorPos(window, xpos, ypos);
            
            // 重置鼠标位置到窗口中心
            glfwSetCursorPos(window, WIDTH / 2, HEIGHT / 2);
            
            // 计算鼠标偏移量
            float xOffset = (float) (xpos[0] - WIDTH / 2);
            float yOffset = (float) (HEIGHT / 2 - ypos[0]);
            
            // 更新玩家视角
            player.processMouseMovement(xOffset * 0.1f, yOffset * 0.1f);
        }
    }
    
    private void update(float deltaTime) {
        // 更新玩家位置
        player.update(deltaTime);
        
        // 更新相机位置(跟随玩家)
        camera.setPosition(new Vector3f(
                player.getPosition().x,
                player.getPosition().y + 1.8f, // 眼睛高度
                player.getPosition().z
        ));
    }
    
    private void render() {
        // 清除颜色和深度缓冲区
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
        
        // 渲染世界
        renderer.render(world, camera);
    }
    
    private void printDebugInfo() {
        System.out.println("===== 调试信息 =====");
        System.out.println("世界大小: " + world.getWorldSizeX() + "x" + world.getWorldSizeY() + "x" + world.getWorldSizeZ());
        System.out.println("玩家位置: " + player.getPosition().x + ", " + player.getPosition().y + ", " + player.getPosition().z);
        System.out.println("相机位置: " + camera.getPosition().x + ", " + camera.getPosition().y + ", " + camera.getPosition().z);
        
        // 检查区块是否生成
        int totalChunks = world.getWorldSizeX() * world.getWorldSizeY() * world.getWorldSizeZ();
        int emptyChunks = 0;
        
        Chunk[][][] chunks = world.getChunks();
        for (int x = 0; x < world.getWorldSizeX(); x++) {
            for (int y = 0; y < world.getWorldSizeY(); y++) {
                for (int z = 0; z < world.getWorldSizeZ(); z++) {
                    Chunk chunk = chunks[x][y][z];
                    if (chunk.getVertexCount() == 0) {
                        emptyChunks++;
                    }
                }
            }
        }
        
        System.out.println("总区块数: " + totalChunks);
        System.out.println("空区块数: " + emptyChunks);
        System.out.println("====================");
    }
}

src\main\java\com\minecraft\entity\Entity.java

package com.minecraft.entity;

import org.joml.Vector3f;

/**
 * 表示游戏世界中的实体基类
 * 所有生物和玩家都继承自此类
 */
public abstract class Entity {
    
    // 实体位置
    protected Vector3f position;
    
    // 实体朝向(欧拉角)
    protected float yaw;   // 水平旋转角度
    protected float pitch; // 垂直旋转角度
    
    // 实体大小
    protected float width;
    protected float height;
    
    // 实体移动速度
    protected float speed;
    
    // 实体是否在地面上
    protected boolean onGround;
    
    // 重力和垂直速度
    protected float gravity = 0.05f;
    protected float verticalVelocity = 0.0f;
    
    public Entity(Vector3f position, float width, float height, float speed) {
        this.position = position;
        this.width = width;
        this.height = height;
        this.speed = speed;
        this.yaw = 0.0f;
        this.pitch = 0.0f;
        this.onGround = false;
    }
    
    /**
     * 更新实体状态
     * @param deltaTime 时间增量
     */
    public void update(float deltaTime) {
        // 应用重力
        if (!onGround) {
            verticalVelocity -= gravity * deltaTime;
        } else {
            verticalVelocity = 0;
        }
        
        // 更新位置
        position.y += verticalVelocity;
        
        // 简单的地面检测(假设地面在y=0)
        if (position.y <= 0) {
            position.y = 0;
            onGround = true;
        } else {
            onGround = false;
        }
    }
    
    /**
     * 移动实体
     * @param dx X方向移动量
     * @param dy Y方向移动量
     * @param dz Z方向移动量
     */
    public void move(float dx, float dy, float dz) {
        position.x += dx;
        position.y += dy;
        position.z += dz;
    }
    
    /**
     * 跳跃
     */
    public void jump() {
        if (onGround) {
            verticalVelocity = 0.2f;
            onGround = false;
        }
    }
    
    // Getters and Setters
    
    public Vector3f getPosition() {
        return position;
    }
    
    public void setPosition(Vector3f position) {
        this.position = position;
    }
    
    public float getYaw() {
        return yaw;
    }
    
    public void setYaw(float yaw) {
        this.yaw = yaw;
    }
    
    public float getPitch() {
        return pitch;
    }
    
    public void setPitch(float pitch) {
        this.pitch = pitch;
    }
    
    public float getWidth() {
        return width;
    }
    
    public float getHeight() {
        return height;
    }
    
    public float getSpeed() {
        return speed;
    }
    
    public void setSpeed(float speed) {
        this.speed = speed;
    }
    
    public boolean isOnGround() {
        return onGround;
    }
}

src\main\java\com\minecraft\entity\Mob.java

package com.minecraft.entity;

import org.joml.Vector3f;
import java.util.Random;

/**
 * 表示游戏中的生物
 */
public class Mob extends Entity {
    
    // 生物类型枚举
    public enum MobType {
        SHEEP(new Vector3f(0.9f, 0.9f, 0.9f), 0.9f, 1.3f, 1.2f),
        PIG(new Vector3f(1.0f, 0.6f, 0.6f), 0.9f, 0.9f, 1.0f),
        CHICKEN(new Vector3f(1.0f, 1.0f, 0.6f), 0.6f, 0.8f, 1.4f),
        COW(new Vector3f(0.6f, 0.4f, 0.2f), 0.9f, 1.4f, 0.8f),
        ZOMBIE(new Vector3f(0.0f, 0.8f, 0.0f), 0.6f, 1.8f, 1.5f);
        
        private final Vector3f color;
        private final float width;
        private final float height;
        private final float speed;
        
        MobType(Vector3f color, float width, float height, float speed) {
            this.color = color;
            this.width = width;
            this.height = height;
            this.speed = speed;
        }
        
        public Vector3f getColor() {
            return color;
        }
        
        public float getWidth() {
            return width;
        }
        
        public float getHeight() {
            return height;
        }
        
        public float getSpeed() {
            return speed;
        }
    }
    
    // 生物类型
    private MobType type;
    
    // 随机数生成器
    private Random random = new Random();
    
    // AI相关属性
    private float moveTimer;
    private float moveDirection;
    private boolean isMoving;
    
    public Mob(Vector3f position, MobType type) {
        super(position, type.getWidth(), type.getHeight(), type.getSpeed());
        this.type = type;
        this.moveTimer = 0;
        this.moveDirection = 0;
        this.isMoving = false;
    }
    
    @Override
    public void update(float deltaTime) {
        super.update(deltaTime);
        
        // 简单的AI行为
        updateAI(deltaTime);
    }
    
    /**
     * 更新生物AI
     */
    private void updateAI(float deltaTime) {
        // 更新移动计时器
        moveTimer -= deltaTime;
        
        // 如果计时器结束,改变移动状态
        if (moveTimer <= 0) {
            // 随机决定是否移动
            isMoving = random.nextFloat() < 0.7f;
            
            // 如果决定移动,随机选择方向
            if (isMoving) {
                moveDirection = random.nextFloat() * 360.0f;
                yaw = moveDirection;
            }
            
            // 重置计时器(2-5秒)
            moveTimer = 2.0f + random.nextFloat() * 3.0f;
        }
        
        // 如果正在移动,则沿当前方向移动
        if (isMoving) {
            float radDirection = (float) Math.toRadians(moveDirection);
            float dx = (float) Math.cos(radDirection) * speed * deltaTime;
            float dz = (float) Math.sin(radDirection) * speed * deltaTime;
            
            move(dx, 0, dz);
            
            // 随机跳跃
            if (onGround && random.nextFloat() < 0.01f) {
                jump();
            }
        }
    }
    
    /**
     * 获取生物类型
     */
    public MobType getType() {
        return type;
    }
    
    /**
     * 获取生物颜色
     */
    public Vector3f getColor() {
        return type.getColor();
    }
}

src\main\java\com\minecraft\entity\Player.java

package com.minecraft.entity;

import org.joml.Vector3f;

/**
 * 表示玩家角色
 */
public class Player extends Entity {
    
    // 移动方向枚举
    public enum Movement {
        FORWARD,
        BACKWARD,
        LEFT,
        RIGHT,
        UP,
        DOWN
    }
    
    // 玩家相机方向向量
    private Vector3f front;
    private Vector3f right;
    private Vector3f up;
    
    // 是否在飞行模式
    private boolean flyMode = true;
    
    public Player(Vector3f position) {
        super(position, 0.6f, 1.8f, 5.0f);
        
        // 初始化方向向量
        front = new Vector3f(0.0f, 0.0f, -1.0f);
        right = new Vector3f(1.0f, 0.0f, 0.0f);
        up = new Vector3f(0.0f, 1.0f, 0.0f);
    }
    
    /**
     * 处理键盘输入
     * @param movement 移动方向
     * @param deltaTime 时间增量
     */
    public void processKeyboard(Movement movement, float deltaTime) {
        float velocity = speed * deltaTime;
        
        switch (movement) {
            case FORWARD:
                position.x += front.x * velocity;
                if (flyMode) position.y += front.y * velocity;
                position.z += front.z * velocity;
                break;
            case BACKWARD:
                position.x -= front.x * velocity;
                if (flyMode) position.y -= front.y * velocity;
                position.z -= front.z * velocity;
                break;
            case LEFT:
                position.x -= right.x * velocity;
                if (flyMode) position.y -= right.y * velocity;
                position.z -= right.z * velocity;
                break;
            case RIGHT:
                position.x += right.x * velocity;
                if (flyMode) position.y += right.y * velocity;
                position.z += right.z * velocity;
                break;
            case UP:
                if (flyMode) {
                    position.y += velocity;
                } else if (onGround) {
                    jump();
                }
                break;
            case DOWN:
                if (flyMode) {
                    position.y -= velocity;
                }
                break;
        }
    }
    
    /**
     * 处理鼠标移动
     * @param xoffset X方向偏移
     * @param yoffset Y方向偏移
     */
    public void processMouseMovement(float xoffset, float yoffset) {
        float sensitivity = 0.1f;
        xoffset *= sensitivity;
        yoffset *= sensitivity;
        
        yaw += xoffset;
        pitch -= yoffset;
        
        // 限制俯仰角度,防止万向节锁
        if (pitch > 89.0f) {
            pitch = 89.0f;
        }
        if (pitch < -89.0f) {
            pitch = -89.0f;
        }
        
        // 更新方向向量
        updateVectors();
    }
    
    /**
     * 更新方向向量
     */
    private void updateVectors() {
        // 计算新的前方向向量
        float radYaw = (float) Math.toRadians(yaw);
        float radPitch = (float) Math.toRadians(pitch);
        
        front.x = (float) (Math.cos(radPitch) * Math.cos(radYaw));
        front.y = (float) Math.sin(radPitch);
        front.z = (float) (Math.cos(radPitch) * Math.sin(radYaw));
        front.normalize();
        
        // 计算右和上方向向量
        right.x = (float) Math.cos(radYaw - Math.PI / 2);
        right.y = 0;
        right.z = (float) Math.sin(radYaw - Math.PI / 2);
        right.normalize();
        
        up.x = 0;
        up.y = 1;
        up.z = 0;
    }
    
    /**
     * 切换飞行模式
     */
    public void toggleFlyMode() {
        flyMode = !flyMode;
    }
    
    /**
     * 获取前方向向量
     */
    public Vector3f getFront() {
        return front;
    }
    
    /**
     * 获取右方向向量
     */
    public Vector3f getRight() {
        return right;
    }
    
    /**
     * 获取上方向向量
     */
    public Vector3f getUp() {
        return up;
    }
    
    /**
     * 检查是否在飞行模式
     */
    public boolean isFlyMode() {
        return flyMode;
    }
    
    @Override
    public void update(float deltaTime) {
        if (!flyMode) {
            super.update(deltaTime);
        }
    }
}

src\main\java\com\minecraft\render\Camera.java

package com.minecraft.render;

import org.joml.Matrix4f;
import org.joml.Vector3f;

/**
 * 表示游戏中的相机
 * 负责计算视图和投影矩阵
 */
public class Camera {
    
    // 相机位置
    private Vector3f position;
    
    // 相机朝向(欧拉角)
    private float yaw;   // 水平旋转角度
    private float pitch; // 垂直旋转角度
    
    // 相机方向向量
    private Vector3f front;
    private Vector3f up;
    private Vector3f right;
    
    // 相机视野角度
    private float fov = 70.0f;
    
    // 视图和投影矩阵
    private Matrix4f viewMatrix;
    private Matrix4f projectionMatrix;
    
    // 视口宽高比
    private float aspectRatio = 16.0f / 9.0f;
    
    public Camera(Vector3f position) {
        this.position = position;
        this.yaw = -90.0f; // 默认朝向-Z方向
        this.pitch = 0.0f;
        
        this.front = new Vector3f(0.0f, 0.0f, -1.0f);
        this.up = new Vector3f(0.0f, 1.0f, 0.0f);
        this.right = new Vector3f(1.0f, 0.0f, 0.0f);
        
        this.viewMatrix = new Matrix4f();
        this.projectionMatrix = new Matrix4f();
        
        updateCameraVectors();
    }
    
    /**
     * 处理鼠标移动
     * @param xoffset X方向偏移
     * @param yoffset Y方向偏移
     */
    public void processMouseMovement(float xoffset, float yoffset) {
        float sensitivity = 0.1f;
        xoffset *= sensitivity;
        yoffset *= sensitivity;
        
        yaw += xoffset;
        pitch -= yoffset;
        
        // 限制俯仰角度,防止万向节锁
        if (pitch > 89.0f) {
            pitch = 89.0f;
        }
        if (pitch < -89.0f) {
            pitch = -89.0f;
        }
        
        // 更新相机方向向量
        updateCameraVectors();
    }
    
    /**
     * 更新相机方向向量
     */
    private void updateCameraVectors() {
        // 计算新的前方向向量
        float radYaw = (float) Math.toRadians(yaw);
        float radPitch = (float) Math.toRadians(pitch);
        
        front.x = (float) (Math.cos(radPitch) * Math.cos(radYaw));
        front.y = (float) Math.sin(radPitch);
        front.z = (float) (Math.cos(radPitch) * Math.sin(radYaw));
        front.normalize();
        
        // 计算右和上方向向量
        right.x = (float) Math.cos(radYaw - Math.PI / 2);
        right.y = 0;
        right.z = (float) Math.sin(radYaw - Math.PI / 2);
        right.normalize();
        
        up.x = 0;
        up.y = 1;
        up.z = 0;
    }
    
    /**
     * 计算视图矩阵
     */
    public Matrix4f getViewMatrix() {
        Vector3f target = new Vector3f();
        position.add(front, target);
        
        viewMatrix.identity();
        viewMatrix.lookAt(position, target, up);
        
        return viewMatrix;
    }
    
    /**
     * 计算投影矩阵
     */
    public Matrix4f getProjectionMatrix() {
        projectionMatrix.identity();
        projectionMatrix.perspective((float) Math.toRadians(fov), aspectRatio, 0.1f, 1000.0f);
        
        return projectionMatrix;
    }
    
    /**
     * 设置视口宽高比
     */
    public void setAspectRatio(float width, float height) {
        this.aspectRatio = width / height;
    }
    
    /**
     * 设置相机位置
     */
    public void setPosition(Vector3f position) {
        this.position = position;
    }
    
    /**
     * 获取相机位置
     */
    public Vector3f getPosition() {
        return position;
    }
    
    /**
     * 获取相机朝向
     */
    public Vector3f getFront() {
        return front;
    }
    
    /**
     * 获取相机上方向
     */
    public Vector3f getUp() {
        return up;
    }
    
    /**
     * 获取相机右方向
     */
    public Vector3f getRight() {
        return right;
    }
    
    /**
     * 获取相机水平旋转角度
     */
    public float getYaw() {
        return yaw;
    }
    
    /**
     * 获取相机垂直旋转角度
     */
    public float getPitch() {
        return pitch;
    }
    
    /**
     * 设置相机视野角度
     */
    public void setFov(float fov) {
        this.fov = fov;
    }
    
    /**
     * 获取相机视野角度
     */
    public float getFov() {
        return fov;
    }
}

src\main\java\com\minecraft\render\Renderer.java

package com.minecraft.render;

import com.minecraft.world.Chunk;
import com.minecraft.world.World;
import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;

import java.nio.FloatBuffer;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;

/**
 * 负责渲染游戏世界
 */
public class Renderer {
    
    // 着色器程序ID
    private int shaderProgram;
    
    // 顶点数组对象
    private int vao;
    
    // 顶点缓冲对象
    private int vboVertices;
    private int vboColors;
    
    // 矩阵缓冲
    private FloatBuffer matrixBuffer;
    
    // 矩阵统一变量位置
    private int viewMatrixLocation;
    private int projectionMatrixLocation;
    
    public Renderer(Camera camera) {
        // 创建矩阵缓冲
        matrixBuffer = BufferUtils.createFloatBuffer(16);
        
        // 初始化OpenGL资源
        initShaders();
        initBuffers();
    }
    
    /**
     * 初始化着色器
     */
    private void initShaders() {
        // 顶点着色器源码
        String vertexShaderSource = 
                "#version 330 core\n" +
                "layout (location = 0) in vec3 aPos;\n" +
                "layout (location = 1) in vec3 aColor;\n" +
                "out vec3 fragColor;\n" +
                "uniform mat4 view;\n" +
                "uniform mat4 projection;\n" +
                "void main() {\n" +
                "    gl_Position = projection * view * vec4(aPos, 1.0);\n" +
                "    fragColor = aColor;\n" +
                "}\n";
        
        // 片段着色器源码
        String fragmentShaderSource = 
                "#version 330 core\n" +
                "in vec3 fragColor;\n" +
                "out vec4 FragColor;\n" +
                "void main() {\n" +
                "    FragColor = vec4(fragColor, 1.0);\n" +
                "}\n";
        
        // 创建顶点着色器
        int vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, vertexShaderSource);
        glCompileShader(vertexShader);
        
        // 检查顶点着色器编译错误
        int success = glGetShaderi(vertexShader, GL_COMPILE_STATUS);
        if (success == GL_FALSE) {
            String infoLog = glGetShaderInfoLog(vertexShader);
            System.err.println("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" + infoLog);
        }
        
        // 创建片段着色器
        int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, fragmentShaderSource);
        glCompileShader(fragmentShader);
        
        // 检查片段着色器编译错误
        success = glGetShaderi(fragmentShader, GL_COMPILE_STATUS);
        if (success == GL_FALSE) {
            String infoLog = glGetShaderInfoLog(fragmentShader);
            System.err.println("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" + infoLog);
        }
        
        // 创建着色器程序
        shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
        glLinkProgram(shaderProgram);
        
        // 检查链接错误
        success = glGetProgrami(shaderProgram, GL_LINK_STATUS);
        if (success == GL_FALSE) {
            String infoLog = glGetProgramInfoLog(shaderProgram);
            System.err.println("ERROR::SHADER::PROGRAM::LINKING_FAILED\n" + infoLog);
        }
        
        // 删除着色器,它们已经链接到程序中,不再需要
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);
        
        // 获取矩阵统一变量位置
        viewMatrixLocation = glGetUniformLocation(shaderProgram, "view");
        projectionMatrixLocation = glGetUniformLocation(shaderProgram, "projection");
    }
    
    /**
     * 初始化缓冲区
     */
    private void initBuffers() {
        // 创建顶点数组对象
        vao = glGenVertexArrays();
        glBindVertexArray(vao);
        
        // 创建顶点缓冲对象
        vboVertices = glGenBuffers();
        vboColors = glGenBuffers();
        
        // 设置顶点属性指针
        // 位置属性
        glBindBuffer(GL_ARRAY_BUFFER, vboVertices);
        glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
        glEnableVertexAttribArray(0);
        
        // 颜色属性
        glBindBuffer(GL_ARRAY_BUFFER, vboColors);
        glVertexAttribPointer(1, 3, GL_FLOAT, false, 0, 0);
        glEnableVertexAttribArray(1);
        
        // 解绑VAO
        glBindVertexArray(0);
    }
    
    /**
     * 渲染世界
     */
    public void render(World world, Camera camera) {
        // 使用着色器程序
        glUseProgram(shaderProgram);
        
        // 设置视图和投影矩阵
        Matrix4f viewMatrix = camera.getViewMatrix();
        Matrix4f projectionMatrix = camera.getProjectionMatrix();
        
        // 将矩阵传递给着色器
        viewMatrix.get(matrixBuffer);
        glUniformMatrix4fv(viewMatrixLocation, false, matrixBuffer);
        
        projectionMatrix.get(matrixBuffer);
        glUniformMatrix4fv(projectionMatrixLocation, false, matrixBuffer);
        
        // 绑定VAO
        glBindVertexArray(vao);
        
        // 渲染所有区块
        Chunk[][][] chunks = world.getChunks();
        for (int x = 0; x < world.getWorldSizeX(); x++) {
            for (int y = 0; y < world.getWorldSizeY(); y++) {
                for (int z = 0; z < world.getWorldSizeZ(); z++) {
                    Chunk chunk = chunks[x][y][z];
                    renderChunk(chunk);
                }
            }
        }
        
        // 解绑VAO
        glBindVertexArray(0);
        
        // 解绑着色器程序
        glUseProgram(0);
    }
    
    /**
     * 渲染单个区块
     */
    private void renderChunk(Chunk chunk) {
        // 获取区块的顶点数据
        float[] vertices = chunk.getVertices();
        float[] colors = chunk.getColors();
        int vertexCount = chunk.getVertexCount();
        
        if (vertexCount == 0) {
            return; // 跳过空区块
        }
        
        // 上传顶点数据
        glBindBuffer(GL_ARRAY_BUFFER, vboVertices);
        glBufferData(GL_ARRAY_BUFFER, vertices, GL_DYNAMIC_DRAW);
        
        // 上传颜色数据
        glBindBuffer(GL_ARRAY_BUFFER, vboColors);
        glBufferData(GL_ARRAY_BUFFER, colors, GL_DYNAMIC_DRAW);
        
        // 绘制区块
        glDrawArrays(GL_QUADS, 0, vertexCount);
    }
    
    /**
     * 清理资源
     */
    public void cleanup() {
        // 删除着色器程序
        glDeleteProgram(shaderProgram);
        
        // 删除VAO和VBO
        glDeleteVertexArrays(vao);
        glDeleteBuffers(vboVertices);
        glDeleteBuffers(vboColors);
    }
}

src\main\java\com\minecraft\render\Texture.java

package com.minecraft.render;

import org.lwjgl.BufferUtils;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL12.GL_CLAMP_TO_EDGE;
import static org.lwjgl.opengl.GL30.glGenerateMipmap;
import static org.lwjgl.stb.STBImage.*;

/**
 * 表示游戏中的纹理
 * 负责加载和管理OpenGL纹理
 */
public class Texture {
    
    // 纹理ID
    private int id;
    
    // 纹理宽度和高度
    private int width;
    private int height;
    
    /**
     * 从文件加载纹理
     * @param path 纹理文件路径
     */
    public Texture(String path) {
        // 生成纹理ID
        id = glGenTextures();
        
        // 绑定纹理
        glBindTexture(GL_TEXTURE_2D, id);
        
        // 设置纹理参数
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        
        IntBuffer width = BufferUtils.createIntBuffer(1);
        IntBuffer height = BufferUtils.createIntBuffer(1);
        IntBuffer channels = BufferUtils.createIntBuffer(1);
        
        // 加载图像
        stbi_set_flip_vertically_on_load(true);
        // 忽略iCCP警告
        stbi_set_unpremultiply_on_load(false);
        stbi_convert_iphone_png_to_rgb(true);
        ByteBuffer image = stbi_load(path, width, height, channels, 4);
        
        if (image != null) {
            this.width = width.get(0);
            this.height = height.get(0);
            
            // 上传纹理数据
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this.width, this.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
            glGenerateMipmap(GL_TEXTURE_2D);
            
            // 释放图像数据
            stbi_image_free(image);
        } else {
            System.err.println("Failed to load texture: " + path);
            System.err.println(stbi_failure_reason());
        }
        
        // 解绑纹理
        glBindTexture(GL_TEXTURE_2D, 0);
    }
    
    /**
     * 创建一个纯色纹理
     * @param r 红色分量 (0-1)
     * @param g 绿色分量 (0-1)
     * @param b 蓝色分量 (0-1)
     */
    public Texture(float r, float g, float b) {
        // 生成纹理ID
        id = glGenTextures();
        
        // 绑定纹理
        glBindTexture(GL_TEXTURE_2D, id);
        
        // 设置纹理参数
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        
        // 创建1x1像素的纹理
        this.width = 1;
        this.height = 1;
        
        // 创建纹理数据
        ByteBuffer data = BufferUtils.createByteBuffer(4);
        data.put((byte) (r * 255));
        data.put((byte) (g * 255));
        data.put((byte) (b * 255));
        data.put((byte) 255);
        data.flip();
        
        // 上传纹理数据
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        
        // 解绑纹理
        glBindTexture(GL_TEXTURE_2D, 0);
    }
    
    /**
     * 绑定纹理
     */
    public void bind() {
        glBindTexture(GL_TEXTURE_2D, id);
    }
    
    /**
     * 解绑纹理
     */
    public void unbind() {
        glBindTexture(GL_TEXTURE_2D, 0);
    }
    
    /**
     * 删除纹理
     */
    public void delete() {
        glDeleteTextures(id);
    }
    
    /**
     * 获取纹理ID
     */
    public int getId() {
        return id;
    }
    
    /**
     * 获取纹理宽度
     */
    public int getWidth() {
        return width;
    }
    
    /**
     * 获取纹理高度
     */
    public int getHeight() {
        return height;
    }
}

src\main\java\com\minecraft\world\Block.java

package com.minecraft.world;

import org.joml.Vector3f;

/**
 * 表示游戏世界中的一个方块
 */
public class Block {
    
    // 方块类型枚举
    public enum BlockType {
        AIR(0, false, new Vector3f(0, 0, 0)),
        GRASS(1, true, new Vector3f(0.2f, 0.8f, 0.2f)),
        DIRT(2, true, new Vector3f(0.6f, 0.3f, 0.0f)),
        STONE(3, true, new Vector3f(0.5f, 0.5f, 0.5f)),
        WATER(4, true, new Vector3f(0.0f, 0.0f, 0.8f)),
        SAND(5, true, new Vector3f(0.8f, 0.8f, 0.0f)),
        WOOD(6, true, new Vector3f(0.6f, 0.3f, 0.3f)),
        LEAVES(7, true, new Vector3f(0.0f, 0.5f, 0.0f));
        
        private final int id;
        private final boolean solid;
        private final Vector3f color;
        
        BlockType(int id, boolean solid, Vector3f color) {
            this.id = id;
            this.solid = solid;
            this.color = color;
        }
        
        public int getId() {
            return id;
        }
        
        public boolean isSolid() {
            return solid;
        }
        
        public Vector3f getColor() {
            return color;
        }
    }
    
    private BlockType type;
    private final int x;
    private final int y;
    private final int z;
    
    public Block(BlockType type, int x, int y, int z) {
        this.type = type;
        this.x = x;
        this.y = y;
        this.z = z;
    }
    
    public BlockType getType() {
        return type;
    }
    
    public void setType(BlockType type) {
        this.type = type;
    }
    
    public int getX() {
        return x;
    }
    
    public int getY() {
        return y;
    }
    
    public int getZ() {
        return z;
    }
    
    public Vector3f getPosition() {
        return new Vector3f(x, y, z);
    }
    
    public Vector3f getColor() {
        return type.getColor();
    }
    
    public boolean isSolid() {
        return type.isSolid();
    }
    
    public boolean isVisible() {
        return type != BlockType.AIR;
    }
}

src\main\java\com\minecraft\world\Chunk.java

package com.minecraft.world;

import org.joml.Vector3f;

/**
 * 表示游戏世界中的一个区块
 * 区块是由多个方块组成的立方体区域
 */
public class Chunk {
    
    // 区块大小常量
    public static final int CHUNK_SIZE = 16;
    public static final int CHUNK_HEIGHT = 128; // 减小高度以节省内存
    
    // 区块坐标
    private final int chunkX;
    private final int chunkZ;
    
    // 存储区块中的所有方块
    private final Block[][][] blocks;
    
    // 顶点数据
    private float[] vertices;
    private float[] colors;
    private int vertexCount;
    
    // 是否需要重新生成网格
    private boolean dirty = true;
    
    public Chunk(int chunkX, int chunkZ) {
        this.chunkX = chunkX;
        this.chunkZ = chunkZ;
        this.blocks = new Block[CHUNK_SIZE][CHUNK_HEIGHT][CHUNK_SIZE];
        
        // 初始化所有方块为空气
        for (int x = 0; x < CHUNK_SIZE; x++) {
            for (int y = 0; y < CHUNK_HEIGHT; y++) {
                for (int z = 0; z < CHUNK_SIZE; z++) {
                    blocks[x][y][z] = new Block(Block.BlockType.AIR, 
                            x + chunkX * CHUNK_SIZE, 
                            y, 
                            z + chunkZ * CHUNK_SIZE);
                }
            }
        }
    }
    
    /**
     * 获取指定位置的方块
     */
    public Block getBlock(int x, int y, int z) {
        if (x < 0 || x >= CHUNK_SIZE || y < 0 || y >= CHUNK_HEIGHT || z < 0 || z >= CHUNK_SIZE) {
            return null;
        }
        return blocks[x][y][z];
    }
    
    /**
     * 设置指定位置的方块类型
     */
    public void setBlockType(int x, int y, int z, Block.BlockType type) {
        if (x < 0 || x >= CHUNK_SIZE || y < 0 || y >= CHUNK_HEIGHT || z < 0 || z >= CHUNK_SIZE) {
            return;
        }
        blocks[x][y][z].setType(type);
        dirty = true; // 标记需要重新生成网格
    }
    
    /**
     * 生成区块网格
     * 只为可见的方块面创建顶点数据
     */
    public void generateMesh() {
        // 计算需要的顶点数量(最多6个面 * 每个面4个顶点 * 区块中的方块数量)
        // 使用更保守的估计,假设只有20%的方块是可见的
        int maxVertices = 6 * 4 * CHUNK_SIZE * CHUNK_HEIGHT * CHUNK_SIZE / 5;
        vertices = new float[maxVertices * 3]; // 每个顶点3个坐标
        colors = new float[maxVertices * 3]; // 每个顶点3个颜色分量
        
        vertexCount = 0;
        
        // 遍历所有方块
        for (int x = 0; x < CHUNK_SIZE; x++) {
            for (int y = 0; y < CHUNK_HEIGHT; y++) {
                for (int z = 0; z < CHUNK_SIZE; z++) {
                    Block block = blocks[x][y][z];
                    
                    // 跳过空气方块
                    if (block.getType() == Block.BlockType.AIR) {
                        continue;
                    }
                    
                    // 检查每个面是否需要渲染(如果相邻方块是透明的,则需要渲染)
                    // 上面
                    if (y == CHUNK_HEIGHT - 1 || blocks[x][y + 1][z].getType() == Block.BlockType.AIR) {
                        addFace(block, 0, 1, 0);
                    }
                    
                    // 下面
                    if (y == 0 || blocks[x][y - 1][z].getType() == Block.BlockType.AIR) {
                        addFace(block, 0, -1, 0);
                    }
                    
                    // 前面
                    if (z == CHUNK_SIZE - 1 || blocks[x][y][z + 1].getType() == Block.BlockType.AIR) {
                        addFace(block, 0, 0, 1);
                    }
                    
                    // 后面
                    if (z == 0 || blocks[x][y][z - 1].getType() == Block.BlockType.AIR) {
                        addFace(block, 0, 0, -1);
                    }
                    
                    // 右面
                    if (x == CHUNK_SIZE - 1 || blocks[x + 1][y][z].getType() == Block.BlockType.AIR) {
                        addFace(block, 1, 0, 0);
                    }
                    
                    // 左面
                    if (x == 0 || blocks[x - 1][y][z].getType() == Block.BlockType.AIR) {
                        addFace(block, -1, 0, 0);
                    }
                }
            }
        }
        
        dirty = false;
    }
    
    /**
     * 添加一个方块面的顶点数据
     */
    private void addFace(Block block, int nx, int ny, int nz) {
        float x = block.getX();
        float y = block.getY();
        float z = block.getZ();
        Vector3f color = block.getColor();
        
        // 检查是否有足够的空间添加新的顶点
        if (vertexCount + 4 >= vertices.length / 3) {
            return; // 如果没有足够空间,跳过这个面
        }
        
        // 根据面的法线方向添加适当的顶点
        if (nx == 1) { // 右面 (+X)
            addVertex(x + 0.5f, y - 0.5f, z - 0.5f, color);
            addVertex(x + 0.5f, y + 0.5f, z - 0.5f, color);
            addVertex(x + 0.5f, y + 0.5f, z + 0.5f, color);
            addVertex(x + 0.5f, y - 0.5f, z + 0.5f, color);
        } else if (nx == -1) { // 左面 (-X)
            addVertex(x - 0.5f, y - 0.5f, z + 0.5f, color);
            addVertex(x - 0.5f, y + 0.5f, z + 0.5f, color);
            addVertex(x - 0.5f, y + 0.5f, z - 0.5f, color);
            addVertex(x - 0.5f, y - 0.5f, z - 0.5f, color);
        } else if (ny == 1) { // 上面 (+Y)
            addVertex(x - 0.5f, y + 0.5f, z - 0.5f, color);
            addVertex(x - 0.5f, y + 0.5f, z + 0.5f, color);
            addVertex(x + 0.5f, y + 0.5f, z + 0.5f, color);
            addVertex(x + 0.5f, y + 0.5f, z - 0.5f, color);
        } else if (ny == -1) { // 下面 (-Y)
            addVertex(x - 0.5f, y - 0.5f, z + 0.5f, color);
            addVertex(x - 0.5f, y - 0.5f, z - 0.5f, color);
            addVertex(x + 0.5f, y - 0.5f, z - 0.5f, color);
            addVertex(x + 0.5f, y - 0.5f, z + 0.5f, color);
        } else if (nz == 1) { // 前面 (+Z)
            addVertex(x + 0.5f, y - 0.5f, z + 0.5f, color);
            addVertex(x + 0.5f, y + 0.5f, z + 0.5f, color);
            addVertex(x - 0.5f, y + 0.5f, z + 0.5f, color);
            addVertex(x - 0.5f, y - 0.5f, z + 0.5f, color);
        } else if (nz == -1) { // 后面 (-Z)
            addVertex(x - 0.5f, y - 0.5f, z - 0.5f, color);
            addVertex(x - 0.5f, y + 0.5f, z - 0.5f, color);
            addVertex(x + 0.5f, y + 0.5f, z - 0.5f, color);
            addVertex(x + 0.5f, y - 0.5f, z - 0.5f, color);
        }
    }
    
    /**
     * 添加一个顶点及其颜色
     */
    private void addVertex(float x, float y, float z, Vector3f color) {
        int index = vertexCount * 3;
        
        vertices[index] = x;
        vertices[index + 1] = y;
        vertices[index + 2] = z;
        
        colors[index] = color.x;
        colors[index + 1] = color.y;
        colors[index + 2] = color.z;
        
        vertexCount++;
    }
    
    public float[] getVertices() {
        if (dirty) {
            generateMesh();
        }
        return vertices;
    }
    
    public float[] getColors() {
        if (dirty) {
            generateMesh();
        }
        return colors;
    }
    
    public int getVertexCount() {
        if (dirty) {
            generateMesh();
        }
        return vertexCount;
    }
    
    public int getChunkX() {
        return chunkX;
    }
    
    public int getChunkZ() {
        return chunkZ;
    }
}

src\main\java\com\minecraft\world\World.java

package com.minecraft.world;

import java.util.Random;

/**
 * 表示游戏世界
 * 管理多个区块组成的完整世界
 */
public class World {
    
    // 世界大小(以区块为单位)
    private final int worldSizeX;
    private final int worldSizeY;
    private final int worldSizeZ;
    
    // 存储所有区块
    private final Chunk[][][] chunks;
    
    // 随机数生成器
    private final Random random = new Random();
    
    public World(int worldSizeX, int worldSizeY, int worldSizeZ) {
        this.worldSizeX = worldSizeX;
        this.worldSizeY = worldSizeY;
        this.worldSizeZ = worldSizeZ;
        
        // 创建区块数组
        chunks = new Chunk[worldSizeX][worldSizeY][worldSizeZ];
        
        // 初始化所有区块
        for (int x = 0; x < worldSizeX; x++) {
            for (int y = 0; y < worldSizeY; y++) {
                for (int z = 0; z < worldSizeZ; z++) {
                    chunks[x][y][z] = new Chunk(x, z);
                }
            }
        }
    }
    
    /**
     * 生成地形
     * 使用简单的噪声函数生成高度图,然后填充方块
     */
    public void generateTerrain() {
        // 生成基础地形
        generateBaseTerrain();
        
        // 生成树木
        generateTrees(30); // 减少树木数量从100到30
    }
    
    /**
     * 生成基础地形
     */
    private void generateBaseTerrain() {
        // 遍历所有区块
        for (int chunkX = 0; chunkX < worldSizeX; chunkX++) {
            for (int chunkZ = 0; chunkZ < worldSizeZ; chunkZ++) {
                // 对区块中的每个X,Z坐标生成高度
                for (int x = 0; x < Chunk.CHUNK_SIZE; x++) {
                    for (int z = 0; z < Chunk.CHUNK_SIZE; z++) {
                        // 计算世界坐标
                        int worldX = chunkX * Chunk.CHUNK_SIZE + x;
                        int worldZ = chunkZ * Chunk.CHUNK_SIZE + z;
                        
                        // 使用简化的噪声函数生成高度
                        int height = generateHeight(worldX, worldZ);
                        
                        // 填充地形
                        for (int y = 0; y < height; y++) {
                            // 确定方块类型
                            Block.BlockType type;
                            
                            if (y == height - 1) {
                                // 顶层使用草方块
                                type = Block.BlockType.GRASS;
                            } else if (y > height - 4) {
                                // 表层下方使用泥土
                                type = Block.BlockType.DIRT;
                            } else {
                                // 深层使用石头
                                type = Block.BlockType.STONE;
                            }
                            
                            // 设置方块类型
                            setBlockType(worldX, y, worldZ, type);
                        }
                        
                        // 添加水(如果高度低于水平面)
                        for (int y = height; y < 32; y++) {
                            if (getBlockType(worldX, y, worldZ) == Block.BlockType.AIR) {
                                setBlockType(worldX, y, worldZ, Block.BlockType.WATER);
                            }
                        }
                    }
                }
            }
        }
    }
    
    /**
     * 生成树木
     */
    private void generateTrees(int count) {
        for (int i = 0; i < count; i++) {
            // 随机选择位置
            int x = random.nextInt(worldSizeX * Chunk.CHUNK_SIZE);
            int z = random.nextInt(worldSizeZ * Chunk.CHUNK_SIZE);
            
            // 找到地面高度
            int y = findHighestBlock(x, z);
            
            // 只在草方块上生成树
            if (getBlockType(x, y, z) == Block.BlockType.GRASS) {
                // 生成树干
                int treeHeight = 4 + random.nextInt(3); // 4-6个方块高
                for (int h = 1; h <= treeHeight; h++) {
                    setBlockType(x, y + h, z, Block.BlockType.WOOD);
                }
                
                // 生成树叶
                for (int dx = -2; dx <= 2; dx++) {
                    for (int dz = -2; dz <= 2; dz++) {
                        for (int dy = treeHeight - 2; dy <= treeHeight + 1; dy++) {
                            // 跳过树干位置
                            if (dx == 0 && dz == 0 && dy < treeHeight) {
                                continue;
                            }
                            
                            // 跳过角落位置(使树叶更圆)
                            if ((Math.abs(dx) == 2 && Math.abs(dz) == 2)) {
                                continue;
                            }
                            
                            // 添加树叶
                            int lx = x + dx;
                            int ly = y + dy;
                            int lz = z + dz;
                            
                            if (getBlockType(lx, ly, lz) == Block.BlockType.AIR) {
                                setBlockType(lx, ly, lz, Block.BlockType.LEAVES);
                            }
                        }
                    }
                }
            }
        }
    }
    
    /**
     * 生成地形高度
     * 使用简化的噪声函数
     */
    private int generateHeight(int x, int z) {
        // 使用简单的正弦函数模拟噪声
        double noise1 = Math.sin(x * 0.08) * Math.sin(z * 0.08) * 10;
        double noise2 = Math.sin(x * 0.02) * Math.sin(z * 0.03) * 20;
        
        // 基础高度
        int baseHeight = 32;
        
        // 计算最终高度,限制最大高度为60
        return Math.min(baseHeight + (int)(noise1 + noise2), 60);
    }
    
    /**
     * 查找指定位置最高的非空气方块的Y坐标
     */
    public int findHighestBlock(int x, int z) {
        for (int y = Chunk.CHUNK_HEIGHT - 1; y >= 0; y--) {
            if (getBlockType(x, y, z) != Block.BlockType.AIR) {
                return y;
            }
        }
        return 0;
    }
    
    /**
     * 获取指定位置的方块类型
     */
    public Block.BlockType getBlockType(int x, int y, int z) {
        Block block = getBlock(x, y, z);
        return block != null ? block.getType() : Block.BlockType.AIR;
    }
    
    /**
     * 设置指定位置的方块类型
     */
    public void setBlockType(int x, int y, int z, Block.BlockType type) {
        // 计算区块坐标
        int chunkX = Math.floorDiv(x, Chunk.CHUNK_SIZE);
        int chunkZ = Math.floorDiv(z, Chunk.CHUNK_SIZE);
        
        // 检查区块坐标是否在世界范围内
        if (chunkX < 0 || chunkX >= worldSizeX || chunkZ < 0 || chunkZ >= worldSizeZ || y < 0 || y >= Chunk.CHUNK_HEIGHT) {
            return;
        }
        
        // 计算区块内坐标
        int localX = Math.floorMod(x, Chunk.CHUNK_SIZE);
        int localZ = Math.floorMod(z, Chunk.CHUNK_SIZE);
        
        // 获取区块
        Chunk chunk = getChunk(chunkX, 0, chunkZ);
        if (chunk != null) {
            chunk.setBlockType(localX, y, localZ, type);
        }
    }
    
    /**
     * 获取指定位置的方块
     */
    public Block getBlock(int x, int y, int z) {
        // 计算区块坐标
        int chunkX = Math.floorDiv(x, Chunk.CHUNK_SIZE);
        int chunkZ = Math.floorDiv(z, Chunk.CHUNK_SIZE);
        
        // 检查区块坐标是否在世界范围内
        if (chunkX < 0 || chunkX >= worldSizeX || chunkZ < 0 || chunkZ >= worldSizeZ || y < 0 || y >= Chunk.CHUNK_HEIGHT) {
            return null;
        }
        
        // 计算区块内坐标
        int localX = Math.floorMod(x, Chunk.CHUNK_SIZE);
        int localZ = Math.floorMod(z, Chunk.CHUNK_SIZE);
        
        // 获取区块
        Chunk chunk = getChunk(chunkX, 0, chunkZ);
        if (chunk != null) {
            return chunk.getBlock(localX, y, localZ);
        }
        
        return null;
    }
    
    /**
     * 获取指定位置的区块
     */
    public Chunk getChunk(int x, int y, int z) {
        if (x < 0 || x >= worldSizeX || y < 0 || y >= worldSizeY || z < 0 || z >= worldSizeZ) {
            return null;
        }
        return chunks[x][y][z];
    }
    
    /**
     * 获取所有区块
     */
    public Chunk[][][] getChunks() {
        return chunks;
    }
    
    /**
     * 获取世界大小
     */
    public int getWorldSizeX() {
        return worldSizeX;
    }
    
    public int getWorldSizeY() {
        return worldSizeY;
    }
    
    public int getWorldSizeZ() {
        return worldSizeZ;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天天进步2015

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值