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;
    }
}
相关推荐
drebander23 分钟前
使用 Java Stream 优雅实现List 转化为Map<key,Map<key,value>>
java·python·list
乌啼霜满天24926 分钟前
Spring 与 Spring MVC 与 Spring Boot三者之间的区别与联系
java·spring boot·spring·mvc
tangliang_cn32 分钟前
java入门 自定义springboot starter
java·开发语言·spring boot
程序猿阿伟32 分钟前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
Grey_fantasy42 分钟前
高级编程之结构化代码
java·spring boot·spring cloud
弗锐土豆1 小时前
工业生产安全-安全帽第二篇-用java语言看看opencv实现的目标检测使用过程
java·opencv·安全·检测·面部
Elaine2023911 小时前
零碎04 MybatisPlus自定义模版生成代码
java·spring·mybatis
小小大侠客1 小时前
IText创建加盖公章的pdf文件并生成压缩文件
java·pdf·itext
一二小选手1 小时前
【MyBatis】全局配置文件—mybatis.xml 创建xml模板
xml·java·mybatis
猿java1 小时前
Linux Shell和Shell脚本详解!
java·linux·shell