安卓小游戏:小板弹球
安卓小游戏:小板弹球
前言
这个是通过自定义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 = ArrayList<Sprite>()// 目标的集合private val mTargetList = ArrayList<Sprite>()// 球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: WeakReference<BombBallGameView> = 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,对应弧度radian)data 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 version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="BombBallGameView"><attr name="length" format="integer"/><attr name="row" format="integer"/><attr name="col" format="integer"/><attr name="ballMask" format="reference"/><attr name="targetMask" format="reference"/><attr name="targetConfig" format="reference"/></declare-styleable><string-array name="BombBallGameConfig"><item>(0,5)</item><item>(0,6)</item><item>(0,7)</item><item>(0,8)</item><item>(0,9)</item><item>(0,10)</item><item>(0,11)</item><item>(0,12)</item><item>(0,13)</item><item>(0,14)</item><item>(1,3)</item><item>(1,5)</item><item>(1,7)</item><item>(1,9)</item><item>(1,11)</item><item>(1,13)</item><item>(1,15)</item></string-array>
</resources>
掩图也还是从Android Studio里面的vector image来的,我觉得还阔以。
res -> drawable -> ic_circle.xml
<vector android:height="24dp" android:tint="#6F6A6A"android:viewportHeight="24" android:viewportWidth="24"android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"><path android:fillColor="@android:color/white" android:pathData="M12,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"/>
</vector>
res -> drawable -> ic_target.xml
<vector android:height="24dp" android:tint="#6F6A6A"android:viewportHeight="24" android:viewportWidth="24"android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"><path android:fillColor="@android:color/white" android:pathData="M12,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"/>
</vector>
layout也说一下,前面都没写layout,这里用到了字符串数组,说下吧
<com.silencefly96.module_views.game.BombBallGameViewandroid:id="@+id/gamaView"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/black"app:ballMask="@drawable/ic_circle"app:targetMask="@drawable/ic_target"app:targetConfig="@array/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]的时候写的,实际也无需修改,因为只是坐标轴对称了,逻辑并没对称。
相关文章:

安卓小游戏:小板弹球
安卓小游戏:小板弹球 前言 这个是通过自定义View实现小游戏的第三篇,是小时候玩的那种五块钱的游戏机上的,和俄罗斯方块很像,小时候觉得很有意思,就模仿了一下。 需求 这里的逻辑就是板能把球弹起来,球…...

7、单行函数
文章目录1 函数的理解1.1 什么是函数1.2 不同DBMS函数的差异1.3 MySQL的内置函数及分类2 数值函数2.1 基本函数2.2 角度与弧度互换函数2.3 三角函数2.4 指数与对数2.5 进制间的转换3 字符串函数4 日期和时间函数4.1 获取日期、时间4.2 日期与时间戳的转换4.3 获取月份、星期、星…...
华为机试题:HJ56 完全数计算(python)
文章目录博主精品专栏导航知识点详解1、input():获取控制台(任意形式)的输入。输出均为字符串类型。1.1、input() 与 list(input()) 的区别、及其相互转换方法2、print() :打印输出。3、整型int() :将指定进制…...

opencv——傅里叶变换、低通与高通滤波及直方图等操作
1、傅里叶变换a、傅里叶变换原理时域分析:以时间为参照进行分析。频域分析:相当于上帝视角一样,看事物层次更高,时域的运动在频域来看就是静止的。eg:投球——时域分析:第1分钟投了3分,第2分钟投…...

【NGINX入门指北】 进阶篇
nginx 进阶篇 文章目录nginx 进阶篇一、Nginx Proxy 服务器1、代理原理2、proxy代理3、proxy缓存一、Nginx Proxy 服务器 1、代理原理 正向代理 内网客户机通过代理访问互联网,通常要设置代理服务器地址和端口。 反向代理 外网用户通过代理访问内网服务器&…...

Python中关于@修饰符、yeild关键词、next()函数的基本功能简述
关于修饰符:其实就是将修饰符下面的函数当成参数传给它上面的函数。 def a(x):print(a)adef b():print(b) 其效果等价为: def a(x):print(a)def b():print(b)a(b())有个记忆诀窍,的下面哪个函数最近,谁就是儿子,谁就…...
结合Coverity扫描Spring Boot项目进行Path Manipulation漏洞修复
本篇介绍使用Coverity 扫描基于Spring Boot 项目中的Path Manipulation 漏洞, 进而解决风险,并且可以通过扫描。 什么样的代码会被扫描有路径操纵风险? 在Spring Boot 项目中, 实验了如下的场景: 1. Control 中 file path 作为参数传递的会被扫描,单纯服务方法不会 场…...

【FFMPEG源码分析】从ffplay源码摸清ffmpeg框架(一)
ffplay入口 ffmpeg\fftools\ffplay.c int main(int argc, char **argv) {/*******************start 动态库加载/网络初始化等**************/int flags;VideoState *is;init_dynload();av_log_set_flags(AV_LOG_SKIP_REPEATED);parse_loglevel(argc, argv, options);/* regis…...
C++蓝桥杯 基础练习,高精度加法,输入两个整数a和b,输出这两个整数的和。a和b都不超过100位。
C蓝桥杯 基础练习,高精度加法 问题描述 输入两个整数a和b,输出这两个整数的和。a和b都不超过100位。 算法描述 由于a和b都比较大,所以不能直接使用语言中的标准数据类型来存储。对于这种问题,一般使用数组来处理。 定义一…...

MySQL面试题:SQL语句的基本语法
MySQL目录一、数据库入门1. 数据管理技术的三个阶段2. 关系型数据库与非关系型数据库3. 四大非关系型数据库a. 基于列的数据库(column-oriented)b. 键值对存储(Key-Value Stores)c. 文档存储(Document Storesÿ…...

Fluid-数据编排能力原理解析
前言本文对Fluid基础功能-数据编排能力进行原理解析。其中涉及到Fluid架构和k8s csi driver相关知识。建议先了解相关概念,为了便于理解,本文使用JuiceFS作为后端runtime引擎。原理概述Fuild数据编排能力,主要是在云原生环境中,能…...

并发线程、锁、ThreadLocal
并发编程并发编程Java内存模型(JMM)并发编程核心问题—可见性、原子性、有序性volatile关键字原子性原子类CAS(Compare-And-Swap 比较并交换)ABA问题Java中的锁乐观锁和悲观锁可重入锁读写锁分段锁自旋锁共享锁/独占锁公平锁/非公平锁偏向锁/轻量级锁/重…...

CMMI-结项管理
结项管理(ProjectClosing Management, PCM)是指在项目开发工作结束后,对项目的有形资产和无形资产进行清算;对项目进行综合评估;总结经验教训等。结项管理过程域是SPP模型的重要组成部分。本规范阐述了结项管理的规程&…...

网络通信协议是什么?
网络通信基本模式 常见的通信模式有如下2种形式:Client-Server(CS) 、 Browser/Server(BS) 实现网络编程关键的三要素 IP地址:设备在网络中的地址,是唯一的标识。 端口:应用程序在设备中唯一的标识。 协议: 数据在网络中传输的…...
阶段5:Java分布式与微服务实战
目录 第33-34周 Spring Cloud电商实战 一、Eureka-server模块开发 1、引入依赖 2、配置文件 3、启动注解 一、Eureka-server模块开发 第33-34周 Spring Cloud电商实战 一、Eureka-server模块开发 1、引入依赖 父项目依赖:cloud-mall-practice springboot的…...

我的创作纪念日
目录 机缘 收获 日常 憧憬 机缘 其实本来从大一上学期后半段(2017)就开始谢谢零星的博客,只不过当时是自己用hexo搭建了一个小网站,还整了个域名:jiayoudangdang.top,虽然这个早就过期; 后来发现了CSDNÿ…...

Qml学习——动态加载控件
最近在学习Qml,但对Qml的各种用法都不太熟悉,总是会搞忘,所以写几篇文章对学习过程中的遇到的东西做一个记录。 学习参考视频:https://www.bilibili.com/video/BV1Ay4y1W7xd?p1&vd_source0b527ff208c63f0b1150450fd7023fd8 目…...

设计模式之职责链模式
什么是职责链模式 职责链模式是避免请求发送者与接受者耦合在一起,让多个对象都可以接受到请求,从而将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理为止。 职责链模式包含以下几个角色: …...
MySQL入门篇-MySQL 8.0 延迟复制
备注:测试数据库版本为MySQL 8.0 这个blog我们来聊聊MySQL 延迟复制 概述 MySQL的复制一般都很快,虽然有时候因为 网络原因、大事务等原因造成延迟,但是这个无法人为控制。 生产中可能会存在主库误操作,导致数据被删除了,Oracl…...

FPGA时序约束与分析 --- 实例教程(1)
注意: 时序约束辅助工具或者相关的TCL命令,都必须在 open synthesis design / open implemention design 后才能有效运行。 1、时序约束辅助工具 2、查看相关时序信息 3、一般的时序约束顺序 1、 时序约束辅助工具(1)时序约束编辑…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...
Python实现简单音频数据压缩与解压算法
Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中,压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言,提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...