Java课设项目

项目简介:射击生存类小游戏

项目采用技术:

游戏引擎: Unity

编程语言: Java

图形处理: NVIDIA PhysX (物理引擎), HDRP (High Definition Render Pipeline)

音效与音乐: FMOD, Wwise

版本控制: Git

功能需求分析:

角色控制:玩家能够使用键盘和鼠标控制角色移动、瞄准和射击。

武器系统:提供多种武器供玩家选择,每种武器有不同的伤害、射速和准确度。

敌人AI:敌人能够智能地追踪玩家,并在必要时进行反击。

生存要素:玩家需要收集资源来维持生命,如食物、水和医疗用品。

游戏难度:随着游戏进程,敌人数量增多、攻击力提升,增加游戏挑战性。

成就与奖励:完成特定任务或击杀特定敌人可以获得奖励或成就。

项目亮点:

真实物理效果:使用NVIDIA PhysX物理引擎,实现逼真的武器后坐力、物体碰撞等效果。

高画质渲染:利用HDRP进行高质量渲染,打造逼真的游戏场景。

丰富多样的武器:从手枪到火箭筒,各种武器供玩家选择。

智能敌人AI:敌人具有复杂的AI逻辑,使游戏更具挑战性。

二、功能架构图

三、个人任务简述

负责输入处理模块,以及图片,动画的视觉制作,路径查找功能的实现,角色远程攻击

1. 完成的任务与功能

简单描述将自己完成的有特色的地方、重难点地方。

|--------|-------------|----------------------|
| 序号 | 完成功能与任务 | 描述 |
| 1 | 路径查找功能 | 使用了路径查找算法,实现了角色的合法移动 |
| 2 | 远程攻击 | 实现了角色的远程攻击方式 |

本人负责功能

* 路径查找功能的实现:

  1. 核心原理 :路径查找算法的核心在于寻找从起点到终点的最优路径。这通常涉及对空间或网络的建模,以及搜索算法的运用。
    1. 搜索算法的选择 :包括深度优先搜索(DFS)、广度优先搜索(BFS)、Dijkstra算法、A*算法等。这些算法各有特点,适用于不同的场景和需求。由于时间有限,只选择了一种算法进行学习。
java 复制代码
package Game;

public class PathNode implements Comparable<PathNode>{
    private Grid stateData;
    private PathNode parentNode;
    private int g, h, f, depth; //g 从起点移动到指定方格的移动代价, h 从指定的方格移动到终点的估算成本

    public PathNode getParentNode() {
        return parentNode;
    }

    public void setParentNode(PathNode parentNode) {
        this.parentNode = parentNode;
    }

    public int getG() {
        return g;
    }

    public void setG(int g) {
        this.g = g;
    }

    public int getH() {
        return h;
    }

    public void setH(int h) {
        this.h = h;
    }

    public int getF() {
        return f;
    }

    public void setF(int f) {
        this.f = f;
    }

    public int getDepth() {
        return depth;
    }

    public void setDepth(int depth) {
        this.depth = depth;
    }

    public Grid getStateData() {
        return stateData;
    }

    public void setStateData(Grid stateData) {
        this.stateData = stateData;
    }

    public PathNode(Grid stateData, PathNode parentNode, int g, int h, int depth) {
        this.stateData = stateData;
        this.parentNode = parentNode;
        this.g = g;
        this.h = h;
        this.f = this.g + this.h;
        this.depth = depth;
    }

    @Override
    public int compareTo(PathNode other)
    {
        int NodeComp = (this.f - other.getF()) * -1;
        if (NodeComp == 0)
        {
            NodeComp = (this.depth - other.getDepth());
        }
        return NodeComp;
    }
}
java 复制代码
package Game;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class Pathfinder{
    private List<PathNode> openNodes;
    private List<PathNode> closedNodes;
    private List<PathNode> nodesToGoal;
    private List<Grid> pathToGoal;
    private WorldGrids worldGrids;
    private int depth;
    private GameObject object;
    private List<Grid> gridsOfObject;
    private int centre;

    public Pathfinder(WorldGrids worldGrids, GameObject object) {
        this.worldGrids = worldGrids;
        this.object = object;
        this.pathToGoal = new ArrayList<>();
        this.nodesToGoal = new ArrayList<>();
        this.gridsOfObject = worldGrids.getGrid(object);
        Grid tmp = this.worldGrids.getGrid(object.getX(), object.getY());
        for(int i = 0; i < gridsOfObject.size(); i++){
            if(gridsOfObject.get(i).equals(tmp)){
                centre = i;
                break;
            }
        }
    }

    public List<PathNode> getOpenNodes() {
        return openNodes;
    }

    public void setOpenNodes(List<PathNode> openNodes) {
        this.openNodes = openNodes;
    }

    public List<PathNode> getClosedNodes() {
        return closedNodes;
    }

    public void setClosedNodes(List<PathNode> closedNodes) {
        this.closedNodes = closedNodes;
    }

    public int getDepth() {
        return depth;
    }

    public void setDepth(int depth) {
        this.depth = depth;
    }

    public List<PathNode> getNodesToGoal() {
        return nodesToGoal;
    }

    public void setNodesToGoal(List<PathNode> nodesToGoal) {
        this.nodesToGoal = nodesToGoal;
    }

    public List<Grid> getPathToGoal() {
        return pathToGoal;
    }

    public void setPathToGoal(List<Grid> pathToGoal) {
        this.pathToGoal = pathToGoal;
    }

    public WorldGrids getWorldGrids() {
        return worldGrids;
    }

    public boolean ownGrid(Grid currentGrid, Grid otherGird){
        int deltaX = currentGrid.getGridX() - getCentreGrid().getGridX();
        int deltaY = currentGrid.getGridY() - getCentreGrid().getGridY();
        for(Grid grid : gridsOfObject){
            Grid tmp = worldGrids.get(grid.getGridX() + deltaX, grid.getGridY() + deltaY);
            if(tmp.equals(otherGird)){
                return true;
            }
        }
        return false;
    }

    public List<Grid> nextMoves(Grid grid){
        List<Grid> moves = new ArrayList<>();
        int[] x = {0, 0, -1, 1, -1, -1, 1, 1};
        int[] y = {-1, 1, 0, 0, -1, 1, 1, -1};
        int deltaX = grid.getGridX() - getCentreGrid().getGridX();
        int deltaY = grid.getGridY() - getCentreGrid().getGridY();
        for(int i = 0; i < 8; i++){
            boolean flag = true;
            for(int j = 0; j < gridsOfObject.size(); j++){
                int nextX = gridsOfObject.get(j).getGridX() + deltaX + x[i];
                int nextY = gridsOfObject.get(j).getGridY() + deltaY + y[i];
                Grid next = worldGrids.get(nextX, nextY);
                if(!ownGrid(grid, next) && !next.isAccessible()){
                    flag = false;
                    break;
                }
            }
            if(flag){
                moves.add(worldGrids.get(grid.getGridX() + x[i], grid.getGridY() + y[i]));
            }
        }
        return moves;
    }

    public int getHeuristic(Grid currentPos, Grid goalPos){
        return (Math.abs(goalPos.getGridX() - currentPos.getGridX()) + Math.abs(goalPos.getGridY() - currentPos.getGridY())) * 10;
    }

    public int getCost(Grid currentPos, Grid goalPos){
        if(Math.abs(goalPos.getGridX() - currentPos.getGridX()) != 0 && Math.abs(goalPos.getGridY() - currentPos.getGridY()) != 0){
            return 14;
        } else {
            return 10;
        }
    }

    public int getDistance(int x1, int y1, int x2, int y2){
        double deltaX = x1 - x2;
        double deltaY = y1 - y2;
        return (int)Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
    }

    public Grid getCentreGrid(){
        return this.gridsOfObject.get(centre);
    }

    public List<Grid> shortestPath(Grid goalPos){
    	if(((Enemy)object).getTarget() == null) return null;
        worldGrids.updateGrids();

        Grid startPos = (Grid) getCentreGrid().clone();

        openNodes = new ArrayList<>();
        closedNodes = new ArrayList<>();
        depth = 0;
        boolean hasGoal = false;

        openNodes.add(new PathNode(startPos, null, 0, getHeuristic(startPos, goalPos), depth));

        while(openNodes.size() != 0){
            closedNodes.add(openNodes.get(openNodes.size() - 1));
            PathNode currentNode = closedNodes.get(closedNodes.size() - 1);
            Grid current = currentNode.getStateData();
            openNodes.remove(openNodes.size() - 1);

            int distance = getDistance(current.getX(), current.getY(), goalPos.getX(), goalPos.getY());
            int r = ((Enemy)object).getTarget().getRadius();
            if (distance < object.getRadius() + r + 10 || currentNode.getDepth() > 25) {
                hasGoal = true;
                break;
            }

            List<Grid> expanded = nextMoves(current);

            NodeLoop:
            for(int i = 0; (i < openNodes.size() || i < closedNodes.size()); i++){
                int s = expanded.size() - 1;
                while(s >= 0){
                    if(i < openNodes.size()){
                        Grid OpenstateData = openNodes.get(i).getStateData();
                        if(OpenstateData.equals(expanded.get(s))){
                            if((currentNode.getG() + getCost(current, OpenstateData)) < openNodes.get(i).getG()){
                                openNodes.get(i).setG(currentNode.getG() + getCost(current, OpenstateData));
                                openNodes.get(i).setH(getHeuristic(expanded.get(s), goalPos));
                                openNodes.get(i).setF(openNodes.get(i).getG() + openNodes.get(i).getH());
                                openNodes.get(i).setParentNode(currentNode);
                            }
                            expanded.remove(s);
                            if (expanded.isEmpty()) {
                                break NodeLoop;
                            }
                            s--;
                            continue ;
                        }
                    }
                    if(i < closedNodes.size()){
                        if (closedNodes.get(i).getStateData().equals(expanded.get(s))){
                            expanded.remove(s);
                            if (expanded.isEmpty())
                            {
                                break NodeLoop;
                            }
                        }
                    }
                    s--;
                }
            }
            if (!expanded.isEmpty()) {
                for (int i = 0; i < expanded.size(); i++) {
                    openNodes.add(new PathNode(
                            expanded.get(i),
                            currentNode,
                            currentNode.getG() + getCost(current, expanded.get(i)),
                            getHeuristic(expanded.get(i), goalPos),
                            currentNode.getDepth() + 1));
                }
            }
            Collections.sort(openNodes);
        }
        try {
            if (hasGoal) {
                int depth = closedNodes.get(closedNodes.size() - 1).getDepth();
                PathNode parent = closedNodes.get(closedNodes.size() - 1);

                for (int s = 0; s <= depth; s++) {
                    nodesToGoal.add(parent);
                    pathToGoal.add(parent.getStateData());
                    parent = nodesToGoal.get(s).getParentNode();
                }
                Collections.reverse(pathToGoal);
                return pathToGoal;
            }
            return null;
        } catch (NullPointerException e){
            if(pathToGoal != null) return pathToGoal;
            return null;
        }
    }
}

监听玩家输入。

java 复制代码
package Game;

public enum Direction {
    LD, RU, LU, RD, L, R, U, D, STOP;

    public static double toDegree(Direction dir){
        switch (dir){
            case R: return 0;
            case RU: return 45;
            case U: return 90;
            case LU: return 135;
            case L: return 180;
            case LD: return 225;
            case D: return 270;
            case RD:  return 315;
            default: return 360;
        }
    }

    public static double getVectorX(Direction dir){
        switch (dir){
            case R: return 1;
            case RU: return Math.cos(Math.toRadians(45));
            case U: return 0;
            case LU: return -Math.cos(Math.toRadians(45));
            case L: return -1;
            case LD: return -Math.cos(Math.toRadians(45));
            case D: return 0;
            case RD:  return Math.cos(Math.toRadians(45));
            default: return -2;
        }
    }

    public static double getVectorY(Direction dir){
        switch (dir){
            case R: return 0;
            case RU: return Math.cos(Math.toRadians(45));
            case U: return 1;
            case LU: return Math.cos(Math.toRadians(45));
            case L: return 0;
            case LD: return -Math.cos(Math.toRadians(45));
            case D: return -1;
            case RD:  return -Math.cos(Math.toRadians(45));
            default: return -2;
        }
    }
}

​​​​​​​远程攻击的设定:

java 复制代码
package Game;

import java.awt.*;

public class Ball extends Weapon implements Cloneable{
    private final int attackRange = 40;
    private boolean ultimateState;
    private int num;
    protected int picOffset;
    private int[] imgOrder = {4,7,5,6,1,2,3,0};

    public Ball(String name, int radius, int speed, int damage, int coldDownTime, Role role, World world){
        super(name, radius, speed, damage, coldDownTime, role, 300, 360,true, world);
    }

    public void initFireball(Direction dir){
        if(this.num <= 0) return;
        this.setDir(dir);
        double cosA = (Math.cos(Math.toRadians(Direction.toDegree(dir))));
        double sinA = -(Math.sin(Math.toRadians(Direction.toDegree(dir))));
        this.x = (int)(this.host.getX() + this.host.getRadius() * cosA);
        this.y = (int)(this.host.getY() + this.host.getRadius() * sinA);
        this.xIncrement = (int)(this.speed * cosA);
        this.yIncrement = (int)(this.speed * sinA);
        world.addObject((Ball) this.clone());
        this.num--;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public void Attack(){
    }

    @Override
    public boolean collisionDetection(GameObject object) {
        return ((!object.equals(this.host)) && !(object instanceof Weapon) && super.collisionDetection(object));
    }

    public void draw(Graphics g){
        int picY = imgOrder[dir == Direction.STOP ? oldDir.ordinal() : dir.ordinal()];
        int picX = maintainState(3);
        this.x += xIncrement;
        this.y += yIncrement;
        drawOneImage(g, this.name, getPicOffset(), this.x, this.y, picX, picY);
        world.collisionDetection(this);
    }

    public void setState(){
        initFireball(this.host.getDir() == Direction.STOP ? host.getOldDir() : host.getDir());
        super.setState();
    }

    public void setUltimateState(){
        if(getNum() < 8) return;
        for(Direction dir : Direction.values()){
            if(dir == Direction.STOP) continue;
            initFireball(dir);
        }
        super.setState();
    }

    public int maintainState(int n){
        this.state++;
        if(state >= n) {
            state = 0;
        }
        return state;
    }

    public void collisionResponse(GameObject object){
        world.removeObject(this);
        resetState();
        object.onAttack(this);
    }

    public int getPicOffset(){
        return picOffset;
    }

    @Override
    public String toString(){
        String strBuf = name;
        strBuf += ":";
        strBuf += String.valueOf(getNum());
        return strBuf;
    }
}
相关推荐
努力也学不会java1 分钟前
【Java并发】深入理解synchronized
java·开发语言·人工智能·juc
TDengine (老段)1 分钟前
TDengine 数学函数 CEIL 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
LB211222 分钟前
Redis 黑马skyout
java·数据库·redis
豐儀麟阁贵29 分钟前
Java知识点储备
java·开发语言
hrrrrb35 分钟前
【Spring Security】Spring Security 密码编辑器
java·hive·spring
豐儀麟阁贵38 分钟前
2.3变量与常量
java·开发语言
兮动人2 小时前
Eureka注册中心通用写法和配置
java·云原生·eureka
爱编程的小白L4 小时前
基于springboot志愿服务管理系统设计与实现(附源码)
java·spring boot·后端
聪明的笨猪猪6 小时前
Java Redis “持久化”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
聪明的笨猪猪6 小时前
Java Redis “核心基础”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试