当前位置: 首页 > news >正文

科技网站建设杭州比较有名的设计公司

科技网站建设,杭州比较有名的设计公司,如果用局域网做网站,搜索引擎营销的名词解释安卓小游戏#xff1a;小板弹球 前言 这个是通过自定义View实现小游戏的第三篇#xff0c;是小时候玩的那种五块钱的游戏机上的#xff0c;和俄罗斯方块很像#xff0c;小时候觉得很有意思#xff0c;就模仿了一下。 需求 这里的逻辑就是板能把球弹起来#xff0c;球…安卓小游戏小板弹球 前言 这个是通过自定义View实现小游戏的第三篇是小时候玩的那种五块钱的游戏机上的和俄罗斯方块很像小时候觉得很有意思就模仿了一下。 需求 这里的逻辑就是板能把球弹起来球在碰撞的时候能把顶部的目标打掉当板没有挡住球掉到了屏幕下面游戏就结束了。核心思想如下 1载入配置读取游戏信息、配置及掩图2启动游戏控制逻辑球体碰到东西有反弹效果3手势控制板的左右移动 效果图 效果图已经把游戏的逻辑玩出来了大致就是这么个玩法就是我感觉这不像一个游戏因为小球的初始方向就决定了游戏结果也许我应该把板的速度和球的方向结合起来创造不一样。 代码 import android.annotation.SuppressLint import android.app.AlertDialog import android.content.Context import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.drawable.Drawable import android.os.Handler import android.os.Looper import android.os.Message import android.util.AttributeSet import android.view.MotionEvent import android.view.View import com.silencefly96.module_views.R import java.lang.ref.WeakReference import kotlin.math.*/*** 弹球游戏view** 1载入配置读取游戏信息、配置及掩图* 2启动游戏控制逻辑球体碰到东西有反弹效果* 3手势控制板的左右移动** author silence* date 2023-02-08*/ class BombBallGameView JvmOverloads constructor(context: Context,attrs: AttributeSet? null,defStyleAttr: Int 0 ): View(context, attrs, defStyleAttr) {companion object {// 游戏更新间隔一秒20次const val GAME_FLUSH_TIME 50L// 目标移动距离const val TARGET_MOVE_DISTANCE 20// 距离计算公式fun getDistance(x1: Int, y1: Int, x2: Int, y2: Int): Float {return sqrt(((x1 - x2).toDouble().pow(2.0) (y1 - y2).toDouble().pow(2.0)).toFloat())}// 两点连线角度计算, (x1, y1) 为起点fun getDegree(x1: Float, y1: Float, x2: Float, y2: Float): Double {// 弧度val radians atan2(y1 - y2, x1 - x2).toDouble()// 从弧度转换成角度return Math.toDegrees(radians)}}// 板的长度private val mLength: Int// 行的数量、间距private val rowNumb: Intprivate var rowDelta 0// 列的数量、间距private val colNumb: Intprivate var colDelta 0// 球的掩图private val mBallMask: Bitmap?// 目标的掩图private val mTargetMask: Bitmap?// 目标的原始配置private val mTargetConfigList ArrayListSprite()// 目标的集合private val mTargetList ArrayListSprite()// 球private val mBall Sprite(0, 0, 0f)// 板private val mBoard Sprite(0, 0, 0f)// 游戏控制器private val mGameController GameController(this)// 上一个触摸点X的坐标private var mLastX 0f// 画笔private val mPaint Paint().apply {color Color.WHITEstrokeWidth 10fstyle Paint.Style.STROKEflags Paint.ANTI_ALIAS_FLAGtextAlign Paint.Align.CENTERtextSize 30f}init {// 读取配置val typedArray context.obtainStyledAttributes(attrs, R.styleable.BombBallGameView)mLength typedArray.getInteger(R.styleable.BombBallGameView_length, 300)rowNumb typedArray.getInteger(R.styleable.BombBallGameView_row, 30)colNumb typedArray.getInteger(R.styleable.BombBallGameView_col, 20)// 球的掩图var drawable typedArray.getDrawable(R.styleable.BombBallGameView_ballMask)mBallMask if (drawable ! null) drawableToBitmap(drawable) else null// 目标的掩图drawable typedArray.getDrawable(R.styleable.BombBallGameView_targetMask)mTargetMask if (drawable ! null) drawableToBitmap(drawable) else null// 读取目标的布局配置val configId typedArray.getResourceId(R.styleable.BombBallGameView_targetConfig, -1)if (configId ! -1) {getTargetConfig(configId)}typedArray.recycle()}private fun drawableToBitmap(drawable: Drawable): Bitmap? {val w drawable.intrinsicWidthval h drawable.intrinsicHeightval config Bitmap.Config.ARGB_8888val bitmap Bitmap.createBitmap(w, h, config)//注意下面三行代码要用到否则在View或者SurfaceView里的canvas.drawBitmap会看不到图val canvas Canvas(bitmap)drawable.setBounds(0, 0, w, h)drawable.draw(canvas)return bitmap}private fun getTargetConfig(configId: Int) {val array resources.getStringArray(configId)try {for (str in array) {// 取出坐标val pos str.substring(1, str.length - 1).split(,)val x pos[0].trim().toInt()val y pos[1].trim().toInt()mTargetConfigList.add(Sprite(x, y, 0f))}}catch (e : Exception) {e.printStackTrace()}// 填入游戏的listmTargetList.clear()mTargetList.addAll(mTargetConfigList)}override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {super.onSizeChanged(w, h, oldw, oldh)// 开始游戏load()}// 加载private fun load() {mGameController.removeMessages(0)// 设置网格rowDelta height / rowNumbcolDelta width / colNumb// 设置球随机朝下的方向mBall.posX width / 2mBall.posY height / 2mBall.degree (Math.random() * 180 180).toFloat()// 设置板mBoard.posX width / 2mBoard.posY height - 50// 将目标集合中的坐标改为实际坐标for (target in mTargetList) {val exactX target.posY * colDelta colDelta / 2val exactY target.posX * rowDelta rowDelta / 2target.posX exactXtarget.posY exactY}mGameController.sendEmptyMessageDelayed(0, GAME_FLUSH_TIME)}// 重新加载private fun reload() {mGameController.removeMessages(0)// 重置mTargetList.clear()mTargetList.addAll(mTargetConfigList)mGameController.isGameOver false// 设置球随机朝下的方向注意因为Y轴朝下应该是180度以内mBall.posX width / 2mBall.posY height / 2mBall.degree (Math.random() * 180 180).toFloat()// 设置板mBoard.posX width / 2mBoard.posY height - 50// 由于mTargetConfigList内对象被load修改了清空并不影响对象不需要再转换了mGameController.sendEmptyMessageDelayed(0, GAME_FLUSH_TIME)}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)// 绘制网格mPaint.strokeWidth 1ffor (i in 0..rowNumb) {canvas.drawLine(0f, rowDelta * i.toFloat(),width.toFloat(), rowDelta * i.toFloat(), mPaint)}for (i in 0..colNumb) {canvas.drawLine(colDelta * i.toFloat(), 0f,colDelta * i.toFloat(), height.toFloat(), mPaint)}mPaint.strokeWidth 10f// 绘制板canvas.drawLine(mBoard.posX - mLength / 2f, mBoard.posY.toFloat(),mBoard.posX mLength / 2f, mBoard.posY.toFloat(), mPaint)// 绘制球canvas.drawBitmap(mBallMask!!, mBall.posX - mBallMask.width / 2f,mBall.posY - mBallMask.height / 2f, mPaint)// 绘制目标物for (target in mTargetList) {canvas.drawBitmap(mTargetMask!!, target.posX - mTargetMask.width / 2f,target.posY - mTargetMask.height / 2f, mPaint)}}SuppressLint(ClickableViewAccessibility)override fun onTouchEvent(event: MotionEvent): Boolean {when(event.action) {MotionEvent.ACTION_DOWN - {mLastX event.x}MotionEvent.ACTION_MOVE - {val len event.x - mLastXval preX mBoard.posX lenif (preX mLength / 2 preX (width - mLength / 2)) {mBoard.posX len.toInt()invalidate()}mLastX event.x}MotionEvent.ACTION_UP - {}}return true}private fun gameOver() {AlertDialog.Builder(context).setTitle(继续游戏).setMessage(请点击确认继续游戏).setPositiveButton(确认) { _, _ - reload() }.setNegativeButton(取消, null).create().show()}// kotlin自动编译为Java静态类控件引用使用弱引用class GameController(view: BombBallGameView): Handler(Looper.getMainLooper()){// 控件引用private val mRef: WeakReferenceBombBallGameView WeakReference(view)// 游戏结束标志internal var isGameOver falseoverride fun handleMessage(msg: Message) {mRef.get()?.let { gameView -// 移动球val radian Math.toRadians(gameView.mBall.degree.toDouble())val deltaX (TARGET_MOVE_DISTANCE * cos(radian)).toInt()val deltaY (TARGET_MOVE_DISTANCE * sin(radian)).toInt()gameView.mBall.posX deltaXgameView.mBall.posY deltaY// 检查反弹碰撞checkRebound(gameView)// 球和目标的碰撞val iterator gameView.mTargetList.iterator()while (iterator.hasNext()) {val target iterator.next()if (checkCollision(gameView.mBall, target,gameView.mBallMask!!, gameView.mTargetMask!!)) {// 与目标碰撞移除该目标并修改球的方向iterator.remove()collide(gameView.mBall, target)break}}// 循环发送消息刷新页面gameView.invalidate()if (!isGameOver) {gameView.mGameController.sendEmptyMessageDelayed(0, GAME_FLUSH_TIME)}else {gameView.gameOver()}}}// 检测碰撞private fun checkCollision(s1: Sprite, s2: Sprite, mask1: Bitmap, mask2: Bitmap): Boolean {// 选较长边的一半作为碰撞半径val len1 if(mask1.width mask1.height) mask1.width / 2f else mask1.height / 2fval len2 if(mask2.width mask2.height) mask2.width / 2f else mask2.height / 2freturn getDistance(s1.posX, s1.posY, s2.posX, s2.posY) (len1 len2)}// 击中目标时获取反弹角度,角度以两球圆心连线对称并加180度private fun collide(ball: Sprite, target: Sprite) {// 圆心连线角度注意向量方向球的方向向上连线以球为起点val lineDegree getDegree(ball.posX.toFloat(), ball.posY.toFloat(),target.posX.toFloat(), target.posY.toFloat())val deltaDegree abs(lineDegree - ball.degree)ball.degree if(lineDegree ball.degree) {2 * deltaDegree.toFloat() 180}else {-2 * deltaDegree.toFloat() 180}}// 击中边缘或者板时反弹角度,反射角度和法线对称方向相反private fun checkRebound(gameView: BombBallGameView) {val ball gameView.mBallval board gameView.mBoard// 左边边缘法线取同向的180度if (ball.posX 0) {val deltaDegree abs(180 - ball.degree)ball.degree if (ball.degree 180) {2 * deltaDegree - 180}else {-2 * deltaDegree - 180}// 右边边缘}else if (ball.posX gameView.width) {val deltaDegree: Floatball.degree if (ball.degree 180) {deltaDegree ball.degree - 0-2 * deltaDegree 180}else {deltaDegree 360 - ball.degree2 * deltaDegree - 180}// 上边边缘}else if(ball.posY 0) {val deltaDegree abs(90 - ball.degree)ball.degree if (ball.degree 90) {2 * deltaDegree 180}else {-2 * deltaDegree 180}// 和板碰撞因为移动距离的关系y不能完全相等}else if (ball.posY gameView.mBallMask!!.height / 2 board.posY) {// 板内if (abs(ball.posX - board.posX) gameView.mLength / 2){val deltaDegree abs(270 - ball.degree)ball.degree if (ball.degree 270) {2 * deltaDegree - 180}else {-2 * deltaDegree - 180}}else {isGameOver true}}}}// 圆心坐标角度方向degree对应弧度radiandata class Sprite(var posX: Int, var posY: Int, var degree: Float)/*** 供外部回收资源*/fun recycle() {mBallMask?.recycle()mTargetMask?.recycle()mGameController.removeMessages(0)} }对应style配置这里rowNunb不能用了和上个贪吃蛇游戏冲突了不能用一样的名称。游戏数据的数组我也写在这里了实际应该分开写的但是小游戏而已就这样吧 res - values - bomb_ball_game_view_style.xml ?xml version1.0 encodingutf-8? resourcesdeclare-styleable nameBombBallGameViewattr namelength formatinteger/attr namerow formatinteger/attr namecol formatinteger/attr nameballMask formatreference/attr nametargetMask formatreference/attr nametargetConfig formatreference//declare-styleablestring-array nameBombBallGameConfigitem(0,5)/itemitem(0,6)/itemitem(0,7)/itemitem(0,8)/itemitem(0,9)/itemitem(0,10)/itemitem(0,11)/itemitem(0,12)/itemitem(0,13)/itemitem(0,14)/itemitem(1,3)/itemitem(1,5)/itemitem(1,7)/itemitem(1,9)/itemitem(1,11)/itemitem(1,13)/itemitem(1,15)/item/string-array /resources掩图也还是从Android Studio里面的vector image来的我觉得还阔以。 res - drawable - ic_circle.xml vector android:height24dp android:tint#6F6A6Aandroid:viewportHeight24 android:viewportWidth24android:width24dp xmlns:androidhttp://schemas.android.com/apk/res/androidpath android:fillColorandroid:color/white android:pathDataM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13h-4v4h-2v-4L7,13v-2h4L11,7h2v4h4v2z/ /vectorres - drawable - ic_target.xml vector android:height24dp android:tint#6F6A6Aandroid:viewportHeight24 android:viewportWidth24android:width24dp xmlns:androidhttp://schemas.android.com/apk/res/androidpath android:fillColorandroid:color/white android:pathDataM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13h-4v4h-2v-4L7,13v-2h4L11,7h2v4h4v2z/ /vectorlayout也说一下前面都没写layout这里用到了字符串数组说下吧 com.silencefly96.module_views.game.BombBallGameViewandroid:idid/gamaViewandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:backgroundcolor/blackapp:ballMaskdrawable/ic_circleapp:targetMaskdrawable/ic_targetapp:targetConfigarray/BombBallGameConfig/主要问题 下面简单讲讲吧主要结构和前面游戏没什么变化就是游戏逻辑变得复杂了很多。 资源加载 和前面一样资源加载就是从styleable配置里面读取设置这里需要额外说明的就是目标的配置文件了。 这里顶部目标是通过外部的配置文件来设置的接受的是一个字符串数组的资源id我这保存在下面 res - values - bomb_ball_game_view_style.xml - BombBallGameConfig 结构是一个坐标需要注意的是要配合row和col使用行数和列数第一个数字表示第几行第二个数字表示第几列。 item(0,5)/item读取的时候是把行标和列标读到了Sprite的posX和posY里面这里是错误的当时在init读取的时候无法获得控件的宽高所以暂时先存放下在onMeasuer - onSizeChanged得到宽高之后在load中对数据进行处理mTargetList游戏操作的列表和mTargetConfigList原始数据列表都保存的是读取到的配置对象即使mTargetList清空了配置对象不变依然保存在mTargetConfigList这里要分清不然reload的时候再处理就大错特错了。 板的移动 这里叫板实际是通过paint画出来的线只是设置的strokeWidth比较粗而已。移动的时候在onTouchEvent的ACTION_MOVE事件中更新板的坐标在onDraw会以它的坐标和长度绘制成“板”。 球对四周的反弹 球的数据保存在Sprite对象里面里面保存了三个变量坐标以及方向。球在四个边的反弹板实际就是下边类似光的反射找到反射面以及反射的法线再以法线对称就得到反射路线了。实际操作上先获取入射方向与法线夹角的绝对值对称到法线另一边再旋转180度掉头就能得到出射方向了。 当然计算的时候要根据实际情况计算尤其是0度和360度作为法线时。 球和目标的碰撞时的反射 球和目标的碰撞就不说了很简单计算下两个中心的距离就行了。这里说下碰撞后的反射问题和上面在四周的反射类似这里也是要通过反射面和法线来决定实际上法线就是两个圆心的连线而且小球和目标碰撞时方向只会向上所以取小球中心为起点目标中心为中点得到法线向量再去计算角度就很简单了。 球的初始随机方向问题 球的初始随机方向我是想让它向上的那应该生成哪个范围的角度呢我们上学的时候X轴向右Y轴向上上半部分角度时[0, 180]那这时候U轴向下了角度范围呢答案很简单了就是[180, 360]上面碰撞的代码实际是我以默认上半区为[0, 180]的时候写的实际也无需修改因为只是坐标轴对称了逻辑并没对称。
http://www.dnsts.com.cn/news/227354.html

相关文章:

  • wordpress多站点统计国外 电子 商务 网站 欣赏
  • 台州网站制作维护月夜直播免费完整版下载
  • 怎么建免费论坛网站如何申请网页域名
  • 可以做调查的网站wordpress获取分类文章
  • 网站制作与app开发哪个要难一点免费连接附近wifi
  • 三沙网站建设南京网站设计是什么
  • 如何重视企业网站的建设新华书店网站建设
  • 5000元做网站值么百度识别图片找图
  • 企业网站哪里可以做徐州制作网站的公司有哪些
  • 运营企业网站网站备案 失败
  • 电商网站项目经验介绍ppt模板沭阳哪里有做网站推广的
  • 做360全景有什么网站网站做流量推广的方式
  • 莆田网站建设开发郑州抖音代运营公司
  • 网站开发工作协议书范本app开发网上app开发
  • 优质校建设网站在线看crm系统
  • 怎样拿电脑做网站网站速度打开慢的原因
  • 建设一个网站大概费用做别墅花园绿化的网站
  • 网站优化需要做什么工作室网页
  • 外贸网站在哪做外链什么样的资质做电子商务网站
  • 网站权重值wordpress评论不能用
  • 响应式网站制作流程图网站模板与网站定制版的区别
  • 如何做高大上的网站 知乎网站制作切片
  • 程序员开发软件如何做好seo
  • 网站建设调研提纲番禺做网站600元
  • 网站建设公司价毕业网站建设开题报告
  • 做网站友汇网免费微网站案例
  • 邯郸网站建设策划方案银川网站建设广告公司
  • 湛江建站服务刚做的网站怎么搜索不出来
  • 元隆盛建设集团有限公司网站科技平台网站建设
  • 怎么做网站视频教程网站建设所用系统