Code Sketch
sniper_0718
import javax.swing._
import java.awt.{Color, Dimension, Graphics, Graphics2D}
import java.awt.event._
import javax.sound.sampled._
class GamePanel extends JPanel with ActionListener {
setPreferredSize(new Dimension(800, 600))
setFocusable(true)
requestFocusInWindow()
// Game States
var gameState = "START" // START, PLAYING, WIN, GAMEOVER
var showMap = false
// Player Stats & Position
var posX = 1.5
var posY = 1.5
var angle = 0.0
var playerHealth = 100
var isJumping = false
var jumpHeight = 0.0
var jumpDirection = 1
// Glue Wall (G Key)
var glueWallActive = false
var glueWallX = 0.0
var glueWallY = 0.0
// Gun Inventory
val guns: scala.collection.immutable.List[String] = scala.collection.immutable.List("Pistol", "AK47", "Sniper", "Shotgun", "SMG")
var selectedGunIndex = 1
var activeGun: Option[String] = Some("AK47")
var gunRotationAngle = 0.0
// Bullets list
var bullets: scala.collection.immutable.List[scala.collection.mutable.Map[String, Double]] = scala.collection.immutable.List()
// 3 Killers placed on the map
var targets = scala.collection.mutable.ListBuffer(
scala.collection.mutable.Map[String, Any]("x" -> 6.5, "y" -> 1.5, "alive" -> true),
scala.collection.mutable.Map[String, Any]("x" -> 3.5, "y" -> 5.5, "alive" -> true),
scala.collection.mutable.Map[String, Any]("x" -> 6.5, "y" -> 5.5, "alive" -> true)
)
// 3D Map Grid: 1 = Wall, 2 = Container (Jump over with SPACE), 0 = Open Space
val worldMap = Array(
Array(1, 1, 1, 1, 1, 1, 1, 1),
Array(1, 0, 0, 2, 0, 0, 0, 1),
Array(1, 0, 1, 0, 1, 0, 2, 1),
Array(1, 0, 0, 0, 2, 0, 0, 1),
Array(1, 0, 1, 0, 1, 0, 0, 1),
Array(1, 0, 2, 0, 0, 0, 0, 1),
Array(1, 1, 1, 1, 1, 1, 1, 1)
)
val timer = new Timer(30, this)
timer.start()
// Gunshot Sound Effect
def playGunshotSound(): Unit = {
new Thread(new Runnable {
def run(): Unit = {
try {
val sampleRate = 8000f
val buf = new Array[Byte](800)
val af = new AudioFormat(sampleRate, 8, 1, true, false)
val td = AudioSystem.getSourceDataLine(af)
td.open(af)
td.start()
for (i <- buf.indices) {
buf(i) = ((Math.random() * 255) - 128).toByte
}
td.write(buf, 0, buf.length)
td.drain()
td.close()
} catch {
case _: Exception =>
}
}
}).start()
}
// Keyboard Controls
addKeyListener(new KeyAdapter {
override def keyPressed(e: KeyEvent): Unit = {
val moveSpeed = 0.2
val rotSpeed = 0.1
e.getKeyCode match {
case KeyEvent.VK_ENTER =>
if (gameState == "START" || gameState == "WIN" || gameState == "GAMEOVER") {
resetGame()
gameState = "PLAYING"
} else {
activeGun = Some(guns(selectedGunIndex))
}
case KeyEvent.VK_F => // Shoot
if (gameState == "PLAYING") {
playGunshotSound()
bullets = bullets :+ scala.collection.mutable.Map(
"x" -> posX,
"y" -> posY,
"dirX" -> Math.cos(angle),
"dirY" -> Math.sin(angle)
)
}
case KeyEvent.VK_SPACE => // Jump over containers
if (gameState == "PLAYING" && !isJumping) {
isJumping = true
}
case KeyEvent.VK_G => // Glue Wall (White Circle)
if (gameState == "PLAYING") {
glueWallActive = true
glueWallX = posX + Math.cos(angle) * 1.0
glueWallY = posY + Math.sin(angle) * 1.0
}
case KeyEvent.VK_M =>
if (gameState == "PLAYING") showMap = !showMap
case KeyEvent.VK_W =>
if (gameState == "PLAYING") {
val newX = posX + Math.cos(angle) * moveSpeed
val newY = posY + Math.sin(angle) * moveSpeed
val tile = worldMap(newY.toInt)(newX.toInt)
if (tile == 0 || (tile == 2 && isJumping)) {
posX = newX; posY = newY
}
}
case KeyEvent.VK_S =>
if (gameState == "PLAYING") {
val newX = posX - Math.cos(angle) * moveSpeed
val newY = posY - Math.sin(angle) * moveSpeed
val tile = worldMap(newY.toInt)(newX.toInt)
if (tile == 0 || (tile == 2 && isJumping)) {
posX = newX; posY = newY
}
}
case KeyEvent.VK_A =>
if (gameState == "PLAYING") angle -= rotSpeed
case KeyEvent.VK_D =>
if (gameState == "PLAYING") angle += rotSpeed
case _ =>
}
repaint()
}
})
// Mouse Listener for Gun Box
addMouseListener(new MouseAdapter {
override def mouseClicked(e: MouseEvent): Unit = {
requestFocusInWindow()
if (gameState == "PLAYING" && e.getX >= 620 && e.getX <= 780 && e.getY >= 20 && e.getY <= 180) {
val clickedIndex = ((e.getY - 40) / 25).toInt
if (clickedIndex >= 0 && clickedIndex < guns.length) {
selectedGunIndex = clickedIndex
activeGun = Some(guns(selectedGunIndex))
repaint()
}
}
}
})
def resetGame(): Unit = {
posX = 1.5
posY = 1.5
angle = 0.0
playerHealth = 100
isJumping = false
jumpHeight = 0.0
glueWallActive = false
bullets = List()
targets(0)("alive") = true
targets(1)("alive") = true
targets(2)("alive") = true
}
override def actionPerformed(e: ActionEvent): Unit = {
if (gameState == "PLAYING") {
gunRotationAngle += 0.08
// Jump Physics Animation
if (isJumping) {
jumpHeight += jumpDirection * 15
if (jumpHeight >= 45) jumpDirection = -1
if (jumpHeight <= 0) {
jumpHeight = 0
jumpDirection = 1
isJumping = false
}
}
// 1. Move Player Bullets
bullets = bullets.map { b =>
b("x") = b("x") + b("dirX") * 0.4
b("y") = b("y") + b("dirY") * 0.4
b
}.filter { b =>
val bx = b("x").toInt
val by = b("y").toInt
bx >= 0 && bx < 8 && by >= 0 && by < 7 && worldMap(by)(bx) != 1
}
// 2. Killers Collision Check & Counter Attack
var activeBullets = List[scala.collection.mutable.Map[String, Double]]()
for (b <- bullets) {
var hit = false
targets.foreach { t =>
if (t("alive").asInstanceOf[Boolean]) {
val dist = Math.hypot(t("x").asInstanceOf[Double] - b("x"), t("y").asInstanceOf[Double] - b("y"))
if (dist < 0.6) {
t("alive") = false
hit = true
}
}
}
if (!hit) activeBullets = activeBullets :+ b
}
bullets = activeBullets
// Killers attack player if close
targets.foreach { t =>
if (t("alive").asInstanceOf[Boolean]) {
val distToPlayer = Math.hypot(t("x").asInstanceOf[Double] - posX, t("y").asInstanceOf[Double] - posY)
if (distToPlayer < 1.2) {
// Check if protected by Glue Wall
val distToGlue = if (glueWallActive) Math.hypot(glueWallX - posX, glueWallY - posY) else 999.0
if (!glueWallActive || distToGlue > 1.0) {
playerHealth -= 1
if (playerHealth <= 0) {
gameState = "GAMEOVER"
}
}
}
}
}
// Check Win condition
if (targets.forall(t => !t("alive").asInstanceOf[Boolean])) {
gameState = "WIN"
}
}
repaint()
}
override def paintComponent(g: Graphics): Unit = {
super.paintComponent(g)
val g2d = g.asInstanceOf[Graphics2D]
val width = getWidth
val height = getHeight
if (gameState == "START") {
g2d.setColor(new Color(40, 20, 10))
g2d.fillRect(0, 0, width, height)
g2d.setColor(Color.WHITE)
g2d.drawString("3D KILLER GAME - CONTAINERS & GLUE WALL", width / 2 - 135, height / 2 - 40)
g2d.setColor(Color.YELLOW)
g2d.drawString("PRESS ENTER TO START", width / 2 - 90, height / 2)
} else if (gameState == "WIN") {
g2d.setColor(new Color(40, 20, 10))
g2d.fillRect(0, 0, width, height)
g2d.setColor(Color.GREEN)
g2d.drawString("VICTORY! ALL KILLERS ELIMINATED.", width / 2 - 110, height / 2 - 20)
g2d.setColor(Color.WHITE)
g2d.drawString("PRESS ENTER TO RESTART", width / 2 - 85, height / 2 + 20)
} else if (gameState == "GAMEOVER") {
g2d.setColor(Color.BLACK)
g2d.fillRect(0, 0, width, height)
g2d.setColor(Color.RED)
g2d.drawString("GAME OVER! KILLERS GOT YOU.", width / 2 - 95, height / 2 - 20)
g2d.setColor(Color.WHITE)
g2d.drawString("PRESS ENTER TO RETRY", width / 2 - 75, height / 2 + 20)
} else {
// Brown Theme Ceiling & Floor (adjusted with jump height)
g2d.setColor(new Color(60, 30, 10))
g2d.fillRect(0, 0, width, (height / 2 + jumpHeight).toInt)
g2d.setColor(new Color(90, 50, 20))
g2d.fillRect(0, (height / 2 + jumpHeight).toInt, width, height - (height / 2 + jumpHeight).toInt)
// Raycasting Walls & Containers
val numRays = width
val fov = Math.PI / 3
for (i <- 0 until numRays) {
val rayAngle = (angle - fov / 2.0) + (i.toDouble / numRays.toDouble) * fov
var distance = 0.0
var hitType = 0 // 1 = Wall, 2 = Container
val eyeX = Math.cos(rayAngle)
val eyeY = Math.sin(rayAngle)
while (hitType == 0 && distance < 16.0) {
distance += 0.05
val testX = (posX + eyeX * distance).toInt
val testY = (posY + eyeY * distance).toInt
if (testX < 0 || testX >= 8 || testY < 0 || testY >= 7) {
hitType = 1
distance = 16.0
} else {
val t = worldMap(testY)(testX)
if (t > 0) hitType = t
}
}
val correctedDist = distance * Math.cos(rayAngle - angle)
val wallHeight = Math.min(height, (height / correctedDist).toInt)
val ceiling = (height / 2.0) - (wallHeight / 2.0) + jumpHeight
if (hitType == 1) {
val shade = Math.max(20, 200 - (distance * 12).toInt)
g2d.setColor(new Color(shade, shade / 2, 10))
} else {
// Container Color (Orange/Yellowish Box)
g2d.setColor(new Color(200, 130, 30))
}
g2d.fillRect(i, ceiling.toInt, 1, wallHeight)
}
// Render Killers in POV
targets.foreach { t =>
if (t("alive").asInstanceOf[Boolean]) {
val tx = t("x").asInstanceOf[Double] - posX
val ty = t("y").asInstanceOf[Double] - posY
var spriteAngle = Math.atan2(ty, tx) - angle
while (spriteAngle < -Math.PI) spriteAngle += 2 * Math.PI
while (spriteAngle > Math.PI) spriteAngle -= 2 * Math.PI
val dist = Math.hypot(tx, ty)
if (dist > 0.2 && Math.abs(spriteAngle) < Math.PI / 2) {
val screenX = ((width / 2.0) + (Math.tan(spriteAngle) * (width / 2.0))).toInt
val spriteHeight = Math.min(height, (height / dist)).toInt
val spriteWidth = spriteHeight
g2d.setColor(Color.RED)
g2d.fillOval(screenX - spriteWidth / 4, (height / 2) - (spriteHeight / 2) + jumpHeight.toInt, spriteWidth / 2, spriteHeight / 2)
g2d.setColor(new Color(150, 0, 0))
g2d.fillRect(screenX - spriteWidth / 5, (height / 2) + jumpHeight.toInt, spriteWidth * 2 / 5, spriteHeight / 2)
}
}
}
// Render Glue Wall if active (White Circle)
if (glueWallActive) {
val gx = glueWallX - posX
val gy = glueWallY - posY
var gAngle = Math.atan2(gy, gx) - angle
while (gAngle < -Math.PI) gAngle += 2 * Math.PI
while (gAngle > Math.PI) gAngle -= 2 * Math.PI
val gDist = Math.hypot(gx, gy)
if (gDist > 0.2 && Math.abs(gAngle) < Math.PI / 2) {
val gScreenX = ((width / 2.0) + (Math.tan(gAngle) * (width / 2.0))).toInt
val gSize = Math.max(10, (100.0 / gDist).toInt)
g2d.setColor(Color.WHITE)
g2d.drawOval(gScreenX - gSize / 2, (height / 2) - gSize / 2 + jumpHeight.toInt, gSize, gSize)
}
}
// Center Crosshair
g2d.setColor(Color.WHITE)
g2d.drawLine(width / 2 - 10, height / 2 + jumpHeight.toInt, width / 2 + 10, height / 2 + jumpHeight.toInt)
g2d.drawLine(width / 2, height / 2 - 10 + jumpHeight.toInt, width / 2, height / 2 + 10 + jumpHeight.toInt)
// Active Gun in Hands
activeGun.foreach { gun =>
g2d.setColor(Color.DARK_GRAY)
g2d.fillRect(width / 2 - 30, height - 120, 60, 100)
g2d.setColor(Color.BLACK)
g2d.fillRect(width / 2 - 10, height - 140, 20, 30)
g2d.setColor(Color.WHITE)
g2d.drawString(s"Equipped: $gun", width / 2 - 50, height - 130)
}
// Health Bar & Inventory Box
g2d.setColor(Color.RED)
g2d.fillRect(20, 15, 180, 25)
g2d.setColor(Color.GREEN)
g2d.fillRect(20, 15, playerHealth * 180 / 100, 25)
g2d.setColor(Color.WHITE)
g2d.drawRect(20, 15, 180, 25)
g2d.drawString(s"Health: $playerHealth%", 70, 32)
// Right-Side Gun Inventory Box
g2d.setColor(new Color(0, 0, 0, 220))
g2d.fillRect(610, 15, 175, 175)
g2d.setColor(Color.LIGHT_GRAY)
g2d.drawRect(610, 15, 175, 175)
g2d.drawString("3D Gun Inventory", 630, 35)
for (i <- guns.indices) {
if (i == selectedGunIndex) g2d.setColor(Color.GREEN) else g2d.setColor(Color.WHITE)
val xOffset = (Math.sin(gunRotationAngle + i) * 8).toInt
g2d.drawString(s"${i + 1}. ${guns(i)}", 625 + xOffset, 70 + (i * 25))
}
// Minimap ('M' to toggle)
if (showMap) {
g2d.setColor(new Color(0, 0, 0, 220))
g2d.fillRect(20, 50, 150, 150)
g2d.setColor(Color.WHITE)
g2d.drawRect(20, 50, 150, 150)
g2d.drawString("MAP (Blue:You|Orange:Box|Red:Killer)", 22, 68)
g2d.setColor(Color.BLUE)
g2d.fillOval(20 + (posX * 18).toInt, 50 + (posY * 18).toInt, 8, 8)
targets.foreach { t =>
if (t("alive").asInstanceOf[Boolean]) {
g2d.setColor(Color.RED)
g2d.fillOval(20 + (t("x").asInstanceOf[Double] * 18).toInt, 50 + (t("y").asInstanceOf[Double] * 18).toInt, 6, 6)
}
}
}
g2d.setColor(Color.WHITE)
g2d.drawString("Controls: WASD=Move | SPACE=Jump Box | F=Shoot | G=Glue Wall | M=Map", 15, 575)
}
}
}
SwingUtilities.invokeLater(new Runnable {
def run(): Unit = {
val frame = new JFrame("True 3D Game - Containers, Jump & Glue Wall")
val panel = new GamePanel()
frame.add(panel)
frame.pack()
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
frame.setResizable(false)
frame.setLocationRelativeTo(null)
frame.setVisible(true)
}
})