mirror of
				https://github.com/ynerant/Level-Editor.git
				synced 2025-10-31 22:54:30 +01:00 
			
		
		
		
	Fix pathfinder, increase performance
This commit is contained in:
		| @@ -24,11 +24,11 @@ case class RawMap(var cases: List[RawCase], var width: Int, var height: Int) { | |||||||
|  |  | ||||||
| 	def getNeighbours(c: RawCase): Iterable[RawCase] = { | 	def getNeighbours(c: RawCase): Iterable[RawCase] = { | ||||||
| 		var list = Nil: List[RawCase] | 		var list = Nil: List[RawCase] | ||||||
| 		list ::= getCase(c.getPosX, c.getPosY + 1) |  | ||||||
| 		list ::= getCase(c.getPosX + 1, c.getPosY) |  | ||||||
| 		list ::= getCase(c.getPosX, c.getPosY - 1) | 		list ::= getCase(c.getPosX, c.getPosY - 1) | ||||||
|  | 		list ::= getCase(c.getPosX + 1, c.getPosY) | ||||||
|  | 		list ::= getCase(c.getPosX, c.getPosY + 1) | ||||||
| 		list ::= getCase(c.getPosX - 1, c.getPosY) | 		list ::= getCase(c.getPosX - 1, c.getPosY) | ||||||
| 		list.filter((_c: RawCase) => _c != null && _c.getCollision.equals(Collision.ANY)) | 		list.filter((_c: RawCase) => _c != null) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	def getCase(x: Int, y: Int): RawCase = { | 	def getCase(x: Int, y: Int): RawCase = { | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| package fr.ynerant.leveleditor.game | package fr.ynerant.leveleditor.game | ||||||
|  |  | ||||||
| import java.awt.event.{MouseEvent, MouseListener} |  | ||||||
| import java.awt._ | import java.awt._ | ||||||
|  | import java.awt.event.{MouseEvent, MouseListener} | ||||||
| import java.util.Random | import java.util.Random | ||||||
|  |  | ||||||
| import fr.ynerant.leveleditor.api.editor.sprites.SpriteRegister | import fr.ynerant.leveleditor.api.editor.sprites.SpriteRegister | ||||||
| @@ -21,6 +21,7 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") { | |||||||
| 	private var reward = 20 | 	private var reward = 20 | ||||||
| 	private var mobs = ListBuffer[Mob]() | 	private var mobs = ListBuffer[Mob]() | ||||||
| 	private var towers = ListBuffer[Tower]() | 	private var towers = ListBuffer[Tower]() | ||||||
|  | 	private val pathFinder = PathFinder(this) | ||||||
| 	final private var basicTower = null: JRadioButton | 	final private var basicTower = null: JRadioButton | ||||||
| 	final private var nullTower = null: JRadioButton | 	final private var nullTower = null: JRadioButton | ||||||
| 	final private var autoTower = null: JRadioButton | 	final private var autoTower = null: JRadioButton | ||||||
| @@ -70,10 +71,16 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") { | |||||||
| 	pane.add(winLabel) | 	pane.add(winLabel) | ||||||
| 	setVisible(true) | 	setVisible(true) | ||||||
| 	new Thread(() => { | 	new Thread(() => { | ||||||
| 		while ( { | 		pathFinder.calculatePath() | ||||||
| 			hp > 0 && (round < 4 || mobs.nonEmpty) |  | ||||||
| 		}) { | 		while (hp > 0 && (round < 4 || mobs.nonEmpty)) { | ||||||
| 			tick() | 			try | ||||||
|  | 				tick() | ||||||
|  | 			catch { | ||||||
|  | 				case e: Throwable => | ||||||
|  | 					e.printStackTrace() | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			try Thread.sleep(50L) | 			try Thread.sleep(50L) | ||||||
| 			catch { | 			catch { | ||||||
| 				case e: InterruptedException => | 				case e: InterruptedException => | ||||||
| @@ -85,10 +92,12 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") { | |||||||
|  |  | ||||||
| 	def getMap: RawMap = map | 	def getMap: RawMap = map | ||||||
|  |  | ||||||
|  | 	def getPathFinder: PathFinder = pathFinder | ||||||
|  |  | ||||||
| 	def tick(): Unit = { | 	def tick(): Unit = { | ||||||
| 		if (mobs.isEmpty && round < 4) { | 		if (mobs.isEmpty && round < 4) { | ||||||
| 			round += 1 | 			round += 1 | ||||||
| 			val nb_mobs = round * (RANDOM.nextInt(16) + 1) | 			val nb_mobs = round * (RANDOM.nextInt(8) + 1) | ||||||
| 			for (_ <- 1 to nb_mobs) { | 			for (_ <- 1 to nb_mobs) { | ||||||
| 				val mob = Mob.getRandomMob | 				val mob = Mob.getRandomMob | ||||||
| 				do mob.move(RANDOM.nextInt(getMap.getWidth / 16), RANDOM.nextInt(getMap.getHeight / 16)) while ( { | 				do mob.move(RANDOM.nextInt(getMap.getWidth / 16), RANDOM.nextInt(getMap.getHeight / 16)) while ( { | ||||||
| @@ -144,11 +153,23 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") { | |||||||
| 			else null | 			else null | ||||||
| 			if (tower == null || tower.getPrice > reward) return | 			if (tower == null || tower.getPrice > reward) return | ||||||
| 			val c = getMap.getCase(x, y) | 			val c = getMap.getCase(x, y) | ||||||
| 			println(x + ", " + y + ", " + tower + ", " + c) |  | ||||||
| 			if (c == null || !c.getCollision.equals(Collision.ANY)) return | 			if (c == null || !c.getCollision.equals(Collision.ANY)) return | ||||||
| 			c.setCollision(Collision.FULL) | 			c.setCollision(Collision.FULL) | ||||||
| 			reward -= tower.getPrice |  | ||||||
| 			towers += tower | 			pathFinder.invalidate() | ||||||
|  |  | ||||||
|  | 			val accessible = getMap.getCases.filter(c => !Collision.FULL.equals(c.getCollision)) | ||||||
|  | 			if (accessible.exists(c => c.getPosX > 0 && pathFinder.nextPos(c.getPosX, c.getPosY) == null) || !accessible.exists(c => c.getPosX == 0 && !c.getCollision.equals(Collision.FULL))) { | ||||||
|  | 				println(accessible.exists(c => c.getPosX > 0 && pathFinder.nextPos(c.getPosX, c.getPosY) == null)) | ||||||
|  | 				println(!accessible.exists(c => c.getPosX == 0 && pathFinder.nextPos(c.getPosX, c.getPosY) != null)) | ||||||
|  | 				// We ensure that the end of the game is accessible from everywhere, the tower should not block the game | ||||||
|  | 				c.setCollision(Collision.ANY) | ||||||
|  | 				pathFinder.invalidate() | ||||||
|  | 			} | ||||||
|  | 			else { | ||||||
|  | 				reward -= tower.getPrice | ||||||
|  | 				towers += tower | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		override def mouseClicked(event: MouseEvent): Unit = { | 		override def mouseClicked(event: MouseEvent): Unit = { | ||||||
|   | |||||||
							
								
								
									
										40
									
								
								src/main/scala/fr/ynerant/leveleditor/game/PathFinder.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/main/scala/fr/ynerant/leveleditor/game/PathFinder.scala
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | package fr.ynerant.leveleditor.game | ||||||
|  |  | ||||||
|  | import java.util | ||||||
|  |  | ||||||
|  | import fr.ynerant.leveleditor.api.editor.{Collision, RawCase} | ||||||
|  |  | ||||||
|  | case class PathFinder(game: GameFrame) { | ||||||
|  | 	var pred: Map[Int, RawCase] = Map(): Map[Int, RawCase] | ||||||
|  |  | ||||||
|  | 	def invalidate(): Unit = calculatePath() | ||||||
|  |  | ||||||
|  | 	def calculatePath(): Unit = { | ||||||
|  | 		pred = Map() | ||||||
|  | 		val queue = new util.ArrayDeque[RawCase] | ||||||
|  |  | ||||||
|  | 		for (i <- 0 until game.getMap.getHeight / 16) { | ||||||
|  | 			val start = game.getMap.getCase(0, i) | ||||||
|  | 			if (!start.getCollision.equals(Collision.FULL)) { | ||||||
|  | 				pred += (coords(start) -> null) | ||||||
|  | 				queue.add(start) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		while (!queue.isEmpty) { | ||||||
|  | 			val visiting = queue.poll | ||||||
|  | 			game.getMap.getNeighbours(visiting).foreach(neighbour => { | ||||||
|  | 				if (neighbour != null && !neighbour.collision.equals(Collision.FULL) && !pred.contains(coords(neighbour))) { | ||||||
|  | 					pred += (coords(neighbour) -> visiting) | ||||||
|  | 					queue.add(neighbour) | ||||||
|  | 				} | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	def coords(rawCase: RawCase): Int = rawCase.getPosY * game.getMap.getWidth / 16 + rawCase.getPosX | ||||||
|  |  | ||||||
|  | 	def nextPos(x: Int, y: Int): RawCase = { | ||||||
|  | 		pred.getOrElse(y * game.getMap.getWidth / 16 + x, null) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -1,6 +1,5 @@ | |||||||
| package fr.ynerant.leveleditor.game.mobs | package fr.ynerant.leveleditor.game.mobs | ||||||
|  |  | ||||||
| import java.util |  | ||||||
| import java.util.Random | import java.util.Random | ||||||
|  |  | ||||||
| import fr.ynerant.leveleditor.api.editor.RawCase | import fr.ynerant.leveleditor.api.editor.RawCase | ||||||
| @@ -76,30 +75,8 @@ abstract class Mob() { | |||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			var visited = Nil: List[RawCase] | 			val newCase: RawCase = game.getPathFinder.nextPos(getX, getY) | ||||||
| 			val queue = new util.ArrayDeque[RawCase] | 			move(newCase.getPosX, newCase.getPosY) | ||||||
| 			var pred = Map(): Map[RawCase, RawCase] |  | ||||||
| 			var last = null: RawCase |  | ||||||
| 			queue.add(current) |  | ||||||
| 			while (!queue.isEmpty) { |  | ||||||
| 				val visiting = queue.poll |  | ||||||
| 				visited ::= visiting |  | ||||||
| 				game.getMap.getNeighbours(visiting).foreach(neighbour => { |  | ||||||
| 					if (neighbour != null && !visited.contains(neighbour)) { |  | ||||||
| 						pred += (neighbour -> visiting) |  | ||||||
| 						queue.add(neighbour) |  | ||||||
| 						if (neighbour.getPosX == 0) { |  | ||||||
| 							last = neighbour |  | ||||||
| 							queue.clear() |  | ||||||
| 							return |  | ||||||
| 						} |  | ||||||
| 					} |  | ||||||
| 				}) |  | ||||||
| 				if (last != null) { |  | ||||||
| 					while (pred(last) != current) last = pred(last) |  | ||||||
| 					move(last.getPosX, last.getPosY) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user