來玩玩打地鼠遊戲,300行代碼不到
- 2019 年 10 月 8 日
- 筆記
所用到的圖片:


圖1代碼:
import javafx.animation.KeyFrame import javafx.animation.Timeline import javafx.event.ActionEvent import javafx.event.EventHandler import javafx.geometry.Pos import javafx.scene.effect.DropShadow import javafx.scene.image.Image import javafx.scene.image.ImageView import javafx.scene.layout.* import javafx.scene.layout.BorderPane.setAlignment import javafx.scene.paint.Color import javafx.scene.paint.Color.rgb import javafx.scene.shape.Line import javafx.scene.text.Font import javafx.scene.text.FontPosture import javafx.scene.text.FontWeight import javafx.util.Duration import tornadofx.* class MyMoleApp : App(MyMole::class) { override fun stop() { find<StartGame>().replaceWith<MyMole>() super.stop() } } class MyMole : View("打地鼠") { override val root = borderpane { setPrefSize(1000.0, 768.0) val back = BackgroundImage(Image("whacAMole/mole.jpg", 1024.0, 768.0, false, true), BackgroundRepeat.REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, BackgroundSize.DEFAULT) background = Background(back) top = label("Whac A Mole") { font = Font.font("Showcard Gothic", FontWeight.EXTRA_BOLD, FontPosture.REGULAR, 100.0) textFill = rgb(150, 46, 5) style = "-fx-text-border-color:rgb(138,95,69);" alignment = Pos.BASELINE_CENTER } setAlignment(top, Pos.CENTER) center = gridpane { vgap = 5.0 hgap = 5.0 alignment = Pos.CENTER button("Start") { background = null textFill = rgb(195, 46, 5) font = Font.font("Bauhaus 93", FontWeight.EXTRA_BOLD, FontPosture.ITALIC, 90.0) setOnMouseEntered { effect = DropShadow() } setOnMouseExited { effect = null } action { replaceWith(StartGame::class) } } } } } class StartGame : View("打地鼠") { private var count = 0 // 打中地鼠數量 private val tfCount = intProperty() //顯示打中地鼠數量 private val msg = stringProperty() private var hammerView by singleAssign<ImageView>() private var ifHit = true //判斷是否可以打地鼠,true時能擊打 private var pause = false val x1 = 256.0 val y1 = 192.0 val xx = 256.0 val yy = 160.0 //第一個地洞位置及間隔 //地鼠圖像 private val mouseList = (0..2).map { imageview("whacAMole/mouse$it.png") { fitWidth = 120.0 fitHeight = 210.0 x = x1 - 70 y = y1 - 90 } } override val root = pane { val pane1 = this setPrefSize(1024.0, 768.0) val eventHandler = EventHandler<ActionEvent> { //地鼠運動 val lineList = (0..2).map { MouseAppear() } lineList.forEach { pane1.add(it) } var i = 0 mouseList.forEach { //地鼠漸變 it.fade(Duration.millis(4000.0 + i * 1000), 0) { fromValue = 1.0 toValue = 0.1 cycleCount = 1 } //地鼠運動 it.follow(Duration.millis(400.0 + i * 200), lineList[i]) { cycleCount = 2 isAutoReverse = true } i++ } //置為能打 ifHit=true // 鎚子打下去、抬起來效果 pane1.setOnMouseReleased { e1 -> hammerView.rotateProperty().animate(endValue = 0.0, duration = 0.1.seconds) } //打中地鼠記錄次數 鼠標事件 pane1.setOnMousePressed { e1 -> hammerView.rotateProperty().animate(endValue = -40.0, duration = 0.1.seconds) var i = 0 lineList.forEach { if (e1.x < it.endX + 80 && e1.x > it.endX - 80 && ifHit && e1.y < it.startY + 110 && e1.y > it.endY - 110) { count++ //文本框輸出 tfCount.value = count ifHit = false //置為不能打 } i++ } } } //動畫 val animation = Timeline(KeyFrame(Duration.millis(1300.0), eventHandler)) //地鼠速度 animation.cycleCount = 30 //地鼠鑽出次數 animation.play() //地洞圖像 imageview("whacAMole/mole.jpg") { fitWidth = 1024.0 fitHeight = 768.0 } hbox(20.0) { addStylesheet(MyStyle::class) alignment = Pos.BOTTOM_CENTER //重新開始按鈕 button("Quit") { background = null setOnMouseEntered { effect = DropShadow() } setOnMouseExited { effect = null } action { animation.stop() //animation1.stop(); count = 0 tfCount.value = 0 msg.value = "" replaceWith(MyMole::class) } } //暫停按鈕 button("Pause/Continue") { background = null setOnMouseEntered { effect = DropShadow() } setOnMouseExited { effect = null } action { if (!pause) { animation.pause() pause = true } else { animation.play() pause = false } } } textfield(tfCount) { setPrefSize(200.0, 40.0) style = "-fx-text-fill:rgb(195,46,5);" background = null font = Font.font("Bauhaus 93", FontWeight.EXTRA_BOLD, FontPosture.ITALIC, 80.0) } } text(msg) { fill = rgb(222, 87, 61) font = Font.font("Kristen ITC", FontWeight.BOLD, FontPosture.ITALIC, 45.0) x = 280.0 y = 700.0 } mouseList.forEach { add(it) } //鎚子圖像 imageview("whacAMole/hammer.png") { hammerView = this fitWidth = 160.0 fitHeight = 200.0 } //事件源必須設在pane上,不能設在圖片上,否則鼠標要放在圖片上才能動 setOnMouseMoved { e -> hammerView.x = e.x - 60 hammerView.y = e.y - 80 } } private fun MouseAppear(): Line { val ran = (Math.random() * 10.0 * 0.9).toInt() var x0 = x1 var y0 = y1 when (ran) { 0 -> { x0 = x1 y0 = y1 } 1 -> { x0 = x1 + xx - 20 y0 = y1 } 2 -> { x0 = x1 + xx * 2 - 20 y0 = y1 } 3 -> { x0 = x1 - 50 y0 = y1 + yy - 15 } 4 -> { x0 = x1 + xx - 20 y0 = y1 + yy } 5 -> { x0 = x1 + xx * 2 - 20 y0 = y1 + yy } 6 -> { x0 = x1 - 60 y0 = y1 + yy * 2 - 10 } 7 -> { x0 = x1 + xx - 20 y0 = y1 + yy * 2 } 8 -> { x0 = x1 + xx * 2 y0 = y1 + yy * 2 } } val line = Line(x0, y0 + 30, x0, y0 - 50) line.stroke = Color.TRANSPARENT return line } } class MyStyle : Stylesheet() { init { button { textFill = rgb(195, 46, 150) prefWidth = 400.px prefHeight = 80.px font = Font.font("Showcard Gothic", FontWeight.EXTRA_BOLD, FontPosture.ITALIC, 30.0) } } }
圖2代碼:
import javafx.animation.KeyFrame import javafx.animation.Timeline import javafx.event.ActionEvent import javafx.event.EventHandler import javafx.geometry.Pos import javafx.scene.effect.DropShadow import javafx.scene.image.ImageView import javafx.scene.paint.Color import javafx.scene.shape.Line import javafx.scene.text.Font import javafx.scene.text.FontPosture import javafx.scene.text.FontWeight import javafx.util.Duration import tornadofx.* class MyMoleApp1 : App(StartGame1::class) class StartGame1 : View("打地鼠") { private var count = 0 // 打中地鼠數量 private val tfCount = intProperty() //顯示打中地鼠數量 private val msg = stringProperty() private var hammerView by singleAssign<ImageView>() private var ifHit = true //判斷是否可以打地鼠,true時能擊打 private var pause = false val x1 = 256.0 val y1 = 192.0 val xx = 256.0 val yy = 160.0 //第一個地洞位置及間隔 //地鼠圖像 private val mouseList = (0..2).map { imageview("whacAMole/mouse$it.png") { fitWidth = 120.0 fitHeight = 110.0 x = x1 - 70 y = y1 -30 } } override val root = stackpane { setPrefSize(1024.0, 768.0) imageview("whacAMole/mole.jpg") { fitWidth = 1024.0 fitHeight = 768.0 } pane { val pane1 = this setPrefSize(1024.0, 768.0) val eventHandler = EventHandler<ActionEvent> { //地鼠運動 val lineList = (0..2).map { MouseAppear() } lineList.forEach { pane1.add(it) } var i = 0 mouseList.forEach { //地鼠漸變 it.fade(Duration.millis(4000.0 + i * 1000), 0) { fromValue = 1.0 toValue = 0.1 cycleCount = 1 } //地鼠運動 it.follow(Duration.millis(400.0 + i * 200), lineList[i]) { cycleCount = 2 isAutoReverse = true } i++ } //置為能打 ifHit = true // 鎚子打下去、抬起來效果 pane1.setOnMouseReleased { e1 -> hammerView.rotateProperty().animate(endValue = 0.0, duration = 0.1.seconds) } //打中地鼠記錄次數 鼠標事件 pane1.setOnMousePressed { e1 -> hammerView.rotateProperty().animate(endValue = -40.0, duration = 0.1.seconds) var i = 0 lineList.forEach { if (e1.x < it.endX + 80 && e1.x > it.endX - 80 && ifHit && e1.y < it.startY + 110 && e1.y > it.endY - 110) { count++ //文本框輸出 tfCount.value = count ifHit = false //置為不能打 } i++ } } } //動畫 val animation = Timeline(KeyFrame(Duration.millis(1300.0), eventHandler)) //地鼠速度 animation.cycleCount = 30 //地鼠鑽出次數 animation.play() //地洞圖像 mouseList.forEach { add(it) } imageview("whacAMole/mask.png") //鎚子圖像 imageview("whacAMole/hammer.png") { hammerView = this fitWidth = 160.0 fitHeight = 200.0 } //事件源必須設在pane上,不能設在圖片上,否則鼠標要放在圖片上才能動 setOnMouseMoved { e -> hammerView.x = e.x - 60 hammerView.y = e.y - 80 } hbox(20.0) { addStylesheet(MyStyle::class) alignment = Pos.BOTTOM_CENTER //重新開始按鈕 button("Quit") { background = null setOnMouseEntered { effect = DropShadow() } setOnMouseExited { effect = null } action { animation.stop() //animation1.stop(); count = 0 tfCount.value = 0 msg.value = "" replaceWith(MyMole::class) } } //暫停按鈕 button("Pause/Continue") { background = null setOnMouseEntered { effect = DropShadow() } setOnMouseExited { effect = null } action { if (!pause) { animation.pause() pause = true } else { animation.play() pause = false } } } textfield(tfCount) { setPrefSize(200.0, 40.0) style = "-fx-text-fill:rgb(195,46,5);" background = null font = Font.font("Bauhaus 93", FontWeight.EXTRA_BOLD, FontPosture.ITALIC, 80.0) } } text(msg) { fill = Color.rgb(222, 87, 61) font = Font.font("Kristen ITC", FontWeight.BOLD, FontPosture.ITALIC, 45.0) x = 280.0 y = 700.0 } } } private fun MouseAppear(): Line { val ran = (Math.random() * 10.0 * 0.9).toInt() var x0 = x1 var y0 = y1 val offset=150 when (ran) { 0 -> { x0 = x1 y0 = y1+offset } 1 -> { x0 = x1 + xx - 20 y0 = y1+offset } 2 -> { x0 = x1 + xx * 2 - 20 y0 = y1+offset } 3 -> { x0 = x1 - 50 y0 = y1 + yy - 15+offset } 4 -> { x0 = x1 + xx - 20 y0 = y1 + yy+offset } 5 -> { x0 = x1 + xx * 2 - 20 y0 = y1 + yy+offset } 6 -> { x0 = x1 - 60 y0 = y1 + yy * 2 - 10+offset } 7 -> { x0 = x1 + xx - 20 y0 = y1 + yy * 2+offset } 8 -> { x0 = x1 + xx * 2 y0 = y1 + yy * 2+offset } } val line = Line(x0, y0 + 30, x0, y0 - 50) line.stroke = Color.TRANSPARENT return line } } class MyStyle1 : Stylesheet() { init { Companion.button { textFill = Color.rgb(195, 46, 150) prefWidth = 400.px prefHeight = 80.px font = Font.font("Showcard Gothic", FontWeight.EXTRA_BOLD, FontPosture.ITALIC, 30.0) } } }