[Script] PathAlgorithm 0.3(寻路算法动画)
-
作者:mumy
B站:https://space.bilibili.com/363239963这是个教学性脚本,可用于学习如何编写寻路
import Color = JVM.java$.awt$.Color; import JavaBoolean = JVM.java$.lang$.Boolean; import JavaFloat = JVM.java$.lang$.Float; import JavaInteger = JVM.java$.lang$.Integer; import JavaString = JVM.java$.lang$.String; import AttackEvent = JVM.net$.ccbluex$.liquidbounce$.event$.AttackEvent; import ClickBlockEvent = JVM.net$.ccbluex$.liquidbounce$.event$.ClickBlockEvent; import JumpEvent = JVM.net$.ccbluex$.liquidbounce$.event$.JumpEvent; import KeyEvent = JVM.net$.ccbluex$.liquidbounce$.event$.KeyEvent; import MotionEvent = JVM.net$.ccbluex$.liquidbounce$.event$.MotionEvent; import MoveEvent = JVM.net$.ccbluex$.liquidbounce$.event$.MoveEvent; import PacketEvent = JVM.net$.ccbluex$.liquidbounce$.event$.PacketEvent; import Render2DEvent = JVM.net$.ccbluex$.liquidbounce$.event$.Render2DEvent; import Render3DEvent = JVM.net$.ccbluex$.liquidbounce$.event$.Render3DEvent; import SlowDownEvent = JVM.net$.ccbluex$.liquidbounce$.event$.SlowDownEvent; import StepEvent = JVM.net$.ccbluex$.liquidbounce$.event$.StepEvent; import StrafeEvent = JVM.net$.ccbluex$.liquidbounce$.event$.StrafeEvent; import UpdateEvent = JVM.net$.ccbluex$.liquidbounce$.event$.UpdateEvent; import WorldEvent = JVM.net$.ccbluex$.liquidbounce$.event$.WorldEvent; import RenderUtils = JVM.net$.ccbluex$.liquidbounce$.utils$.render$.RenderUtils; import BlockValue = JVM.net$.ccbluex$.liquidbounce$.value$.BlockValue; import BoolValue = JVM.net$.ccbluex$.liquidbounce$.value$.BoolValue; import FloatValue = JVM.net$.ccbluex$.liquidbounce$.value$.FloatValue; import IntegerValue = JVM.net$.ccbluex$.liquidbounce$.value$.IntegerValue; import ListValue = JVM.net$.ccbluex$.liquidbounce$.value$.ListValue; import TextValue = JVM.net$.ccbluex$.liquidbounce$.value$.TextValue; import BlockSnow = JVM.net$.minecraft$.block$.BlockSnow; import BlockWeb = JVM.net$.minecraft$.block$.BlockWeb; import GlStateManager = JVM.net$.minecraft$.client$.renderer$.GlStateManager; import AxisAlignedBB = JVM.net$.minecraft$.util$.AxisAlignedBB; import BlockPos = JVM.net$.minecraft$.util$.BlockPos; import GL11 = JVM.org$.lwjgl$.opengl$.GL11; const scriptName = "PathAlgorithm"; const scriptVersion = 0.3; const scriptAuthor = "mumy++"; class PathAlgorithm { private readonly setting = { float: (name: string, def: number, min: number, max: number, object: object = {}) => { return new _AdaptedValue<number, JavaFloat>(new (Java.extend(FloatValue, object))(name, def, min, max)); }, integer: (name: string, def: number, min: number, max: number, object: object = {}) => { return new _AdaptedValue<number, JavaInteger>(new (Java.extend(IntegerValue, object))(name, def, min, max)); }, boolean: (name: string, def: boolean, object: object = {}) => { return new _AdaptedValue<boolean, JavaBoolean>(new (Java.extend(BoolValue, object))(name, def)); }, list: (name: string, values: string[], def: string, object: object = {}) => { return new _AdaptedValue<string, JavaString>(new (Java.extend(ListValue, object))(name, values, def)); }, text: (name: string, def: string, object: object = {}) => { return new _AdaptedValue<string, JavaString>(new (Java.extend(TextValue, object))(name, def)); }, block: (name: string, def: number, object: object = {}) => { return new _AdaptedValue<number, JavaInteger>(new (Java.extend(BlockValue, object))(name, def)); } }; private readonly settings = { setStart: this.setting.boolean("SetStart", false, { onChanged: (oldValue: boolean, newValue: boolean) => { if (!newValue) { return; } const player = mc.thePlayer!; this.start = new PathAlgorithm.Pos(Math.floor(player.posX), Math.floor(player.posY), Math.floor(player.posZ)); this.aStarAlgorithm = null; this.settings.setStart.set(false); } }), setEnd: this.setting.boolean("SetEnd", false, { onChanged: (oldValue: boolean, newValue: boolean) => { if (!newValue) { return; } const player = mc.thePlayer!; this.end = new PathAlgorithm.Pos(Math.floor(player.posX), Math.floor(player.posY), Math.floor(player.posZ)); this.aStarAlgorithm = null; this.settings.setEnd.set(false); } }), gWeight: this.setting.float("GWeight", 1.00005, 0, 2), hWeight: this.setting.float("HWeight", 1, 0, 2), speed: this.setting.integer("Speed", 1, 1, 50), reset: this.setting.boolean("Reset", false, { onChanged: (oldValue: boolean, newValue: boolean) => { if (!newValue) { return; } this.start = null; this.end = null; this.aStarAlgorithm = null; this.settings.reset.set(false); } }) }; private start = <typeof PathAlgorithm.Pos.prototype | null>null; private end = <typeof PathAlgorithm.Pos.prototype | null>null; private aStarAlgorithm = <typeof PathAlgorithm.AStarAlgorithm.prototype | null>null; private readonly openColor = new Color(200, 200, 200); private readonly closeColor = new Color(127, 127, 127); private readonly pathColor = new Color(0, 220, 220); public getName() { return "PathAlgorithm"; } public getDescription() { return "PathAlgorithm-Module, By-mumy"; } public getCategory() { return "Misc"; } public onEnable() { this.start = null; this.end = null; this.aStarAlgorithm = null; } public onDisable() { this.onEnable(); } public onUpdate() { if (this.start != null && this.end != null) { if (this.aStarAlgorithm == null) { this.aStarAlgorithm = new PathAlgorithm.AStarAlgorithm(this.start, this.end, this.settings.hWeight.get(), this.settings.gWeight.get()); } this.aStarAlgorithm.update(this.settings.speed.get()); } } public onRender3D(event: Render3DEvent) { if (this.start != null && this.aStarAlgorithm == null) { const { x, y, z } = this.start; this.drawEntityBox(new AxisAlignedBB(x, y, z, x + 1, y + 1, z + 1), this.openColor); } if (this.end != null && this.aStarAlgorithm?.path == null) { const { x, y, z } = this.end; this.drawEntityBox(new AxisAlignedBB(x, y, z, x + 1, y + 1, z + 1), this.openColor); } if (this.aStarAlgorithm != null) { if (this.aStarAlgorithm.path != null) { for (let pos of this.aStarAlgorithm.path) { const { x, y, z } = pos; this.drawEntityBox(new AxisAlignedBB(x, y, z, x + 1, y + 1, z + 1), this.pathColor); } } else { for (let pos of this.aStarAlgorithm.openList) { const { x, y, z } = pos; this.drawEntityBox(new AxisAlignedBB(x, y, z, x + 1, y + 1, z + 1), this.openColor); } for (let pos of this.aStarAlgorithm.closeList) { const { x, y, z } = pos; this.drawEntityBox(new AxisAlignedBB(x, y, z, x + 1, y + 1, z + 1), this.closeColor); } } } } public onWorld(event: WorldEvent) { moduleManager.getModule(this.getName())!.setState(false); } public addValues(values: _ValueAdapter) { const settings = <{ [ket: string]: _AdaptedValue<unknown, unknown> }>this.settings; for (let key in settings) { values.add(settings[key]); } } private drawEntityBox(entityBox: AxisAlignedBB, color: Color) { const renderManager = mc.getRenderManager()!; GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); RenderUtils.enableGlCap(GL11.GL_BLEND); RenderUtils.disableGlCap(GL11.GL_TEXTURE_2D, GL11.GL_DEPTH_TEST); GL11.glDepthMask(false); RenderUtils.glColor(color.getRed(), color.getGreen(), color.getBlue(), 26); const axisAlignedBB = new AxisAlignedBB(entityBox.minX - renderManager.renderPosX, entityBox.minY - renderManager.renderPosY, entityBox.minZ - renderManager.renderPosZ, entityBox.maxX - renderManager.renderPosX, entityBox.maxY - renderManager.renderPosY, entityBox.maxZ - renderManager.renderPosZ); RenderUtils.drawFilledBox(axisAlignedBB); GL11.glLineWidth(1); RenderUtils.enableGlCap(GL11.GL_LINE_SMOOTH); RenderUtils.glColor(color.getRed(), color.getGreen(), color.getBlue(), 95); RenderUtils.drawSelectionBoundingBox(axisAlignedBB); GlStateManager.resetColor(); GL11.glDepthMask(true); RenderUtils.resetCaps(); } public static Pos = class Pos { public constructor(public x: number, public y: number, public z: number) {} public equals(pos: Pos) { return pos.x === this.x && pos.y === this.y && pos.z === this.z; } } public static Node = class Node extends PathAlgorithm.Pos { public cost = 0; public hCost = 0; public constructor(public x: number, public y: number, public z: number, public parent: Node | null) { super(x, y, z); this.hCost = (parent?.hCost ?? -1) + 1; } } private static AStarAlgorithm = class AStarAlgorithm { private readonly start: typeof PathAlgorithm.Pos.prototype; private readonly end: typeof PathAlgorithm.Pos.prototype; public readonly openList = <typeof PathAlgorithm.Node.prototype[]>[]; public readonly closeList = <typeof PathAlgorithm.Pos.prototype[]>[]; private readonly hWeight: number; private readonly gWeight: number; public path = <typeof PathAlgorithm.Pos.prototype[] | null>null; public constructor(start: typeof PathAlgorithm.Pos.prototype, end: typeof PathAlgorithm.Pos.prototype, hWeight: number, gWeight: number) { this.start = start; this.end = end; this.hWeight = hWeight; this.gWeight = gWeight; this.openList.push(new PathAlgorithm.Node(start.x, start.y, start.z, null)); } public update(loops: number) { if (this.path != null) { return false; } top: while (loops-- > 0) { if (this.openList.length === 0) { return false; } const node = this.getNode()!; for (let pos of this.closeList) { if (node.equals(pos)) { continue top; } } if (!this.canPassable(new BlockPos(node.x, node.y, node.z))) { continue; } if (node.equals(this.end)) { this.path = []; let temp = <typeof PathAlgorithm.Node.prototype | null>node; do { this.path.push(temp!); temp = temp!.parent; } while (temp != null); this.path.reverse(); return false; } this.closeList.push(node); const { x, y, z } = node; this.createNodeToOpenList(x + 1, y, z, node); this.createNodeToOpenList(x, y + 1, z, node); this.createNodeToOpenList(x, y, z + 1, node); this.createNodeToOpenList(x - 1, y, z, node); this.createNodeToOpenList(x, y - 1, z, node); this.createNodeToOpenList(x, y, z - 1, node); } return true; } private createNodeToOpenList(x: number, y: number, z: number, parent: typeof PathAlgorithm.Node.prototype | null) { const node = new PathAlgorithm.Node(x, y, z, parent); const [ xDist, yDist, zDist ] = [Math.abs(node.x - this.end.x), Math.abs(node.y - this.end.y), Math.abs(node.z - this.end.z)]; node.cost = node.hCost * this.gWeight + (xDist + yDist + zDist) * this.hWeight; this.openList.push(node); } private getNode() { let finalIndex = -1; let finalCost = -1; let finalNode = <typeof PathAlgorithm.Node.prototype | null>null; for (let i = this.openList.length - 1; !(i < 0); --i) { const node = this.openList[i]; const cost = node.cost; if (finalNode == null || cost < finalCost) { finalNode = node; finalCost = cost; finalIndex = i; } if (i === 0) { this.openList.splice(finalIndex, 1); } } return finalNode; } private canPassable(blockPos: BlockPos) { const world = mc.theWorld!; const iBlockState = world.getBlockState(blockPos)!; const block = iBlockState.getBlock()!; return block.getCollisionBoundingBox(world, blockPos, iBlockState) == null ? !(block instanceof BlockWeb) : block instanceof BlockSnow && block.isReplaceable(world, blockPos); } } } let scriptModule: any; function onLoad() {} function onEnable() { scriptModule = moduleManager.registerModule(new PathAlgorithm()); } function onDisable() { moduleManager.unregisterModule(scriptModule); }
-
@CookieChinese Very cool.
-
@CookieChinese 十分有趣