这个主意是如何产生的
创建此类游戏的想法恰好是在黑客马拉松期间提出的。该格式假定开发需要一个工作日,即8个小时。为了及时制作原型,我选择了Android SDK。也许游戏引擎会更适合,但我不理解它们。
借助另一种游戏提出了借助情感进行控制的概念:在那里,可以通过改变声音的音量来设置角色的动作。也许有人已经在游戏控制中运用了情绪。但是我知道这样的例子很少,所以我选择了这种格式。
当心大声的视频!
搭建开发环境
我们只需要在计算机上安装Android Studio。如果没有真正的Android设备可以运行,则可以使用启用了网络摄像头的模拟器。
使用ML Kit创建项目
ML Kit是打动黑客马拉松评审团的绝佳工具:您在原型中使用AI!通常,它有助于将基于机器学习的解决方案嵌入到项目中,例如,用于确定框架中对象,翻译和文本识别的功能。
对于我们而言,重要的是ML Kit具有免费的脱机API,用于识别微笑和睁开或闭上的眼睛。
以前,要使用ML Kit创建任何项目,您首先必须在Firebase控制台中注册。现在可以跳过此步骤以使用脱机功能。
Android应用程式
删除不必要的
为了避免从头开始编写使用相机的逻辑,让我们从官方样本中删除不需要的样本。
首先,下载示例并尝试运行。探索人脸检测模式:它看起来像文章预览。
宣言
让我们开始编辑AndroidManifest.xml。删除除第一个活动标签之外的所有活动标签。并将其放置在CameraXLivePreviewActivity中,以立即从相机开始。在android:value属性的值中,我们只保留面孔,以便从APK中排除不必要的资源。
<meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="face"/>
<activity
android:name=".CameraXLivePreviewActivity"
android:exported="true"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
全步差异
相机
节省时间-我们将不会删除不必要的文件,而是将重点放在CameraXLivePreviewActivity屏幕的元素上。
- 在第117行,设置人脸检测模式:
private String selectedModel = FACE_DETECTION;
- 在第118行,打开前置摄像头:
private int lensFacing = CameraSelector.LENS_FACING_FRONT;
- 在第198-199行的onCreate方法的最后,隐藏设置
findViewById( R.id.settings_button ).setVisibility( View.GONE ); findViewById( R.id.control ).setVisibility( View.GONE );
我们可以在这里停下来。但是,如果FPS渲染和面部网格在视觉上分散了注意力,则可以将其关闭,如下所示:
- 在VisionProcessorBase.java文件中,删除第213-215行以隐藏FPS:
graphicOverlay.add( new InferenceInfoGraphic( graphicOverlay, currentLatencyMs, shouldShowFps ? framesPerSecond : null));
- 在FaceDetectorProcessor.java文件中,删除第75–78行以隐藏面部网格:
for (Face face : faces) { graphicOverlay.add(new FaceGraphic(graphicOverlay, face)); logExtrasForTesting(face); }
全步差异
识别情绪
微笑检测默认情况下处于关闭状态,但是启动它非常容易。我们以示例代码为基础并非没有!让我们在一个单独的类中选择所需的参数,并声明侦听器接口:
FaceDetectorProcessor.java
// FaceDetectorProcessor.java
public class FaceDetectorProcessor extends VisionProcessorBase<List<Face>> {
public static class Emotion {
public final float smileProbability;
public final float leftEyeOpenProbability;
public final float rightEyeOpenProbability;
public Emotion(float smileProbability, float leftEyeOpenProbability, float rightEyeOpenProbability) {
this.smileProbability = smileProbability;
this.leftEyeOpenProbability = leftEyeOpenProbability;
this.rightEyeOpenProbability = rightEyeOpenProbability;
}
}
public interface EmotionListener {
void onEmotion(Emotion emotion);
}
private EmotionListener listener;
public void setListener(EmotionListener listener) {
this.listener = listener;
}
@Override
protected void onSuccess(@NonNull List<Face> faces, @NonNull GraphicOverlay graphicOverlay) {
if (!faces.isEmpty() && listener != null) {
Face face = faces.get(0);
if (face.getSmilingProbability() != null &&
face.getLeftEyeOpenProbability() != null && face.getRightEyeOpenProbability() != null) {
listener.onEmotion(new Emotion(
face.getSmilingProbability(),
face.getLeftEyeOpenProbability(),
face.getRightEyeOpenProbability()
));
}
}
}
}
要启用情感分类,请在CameraXLivePreviewActivity类中设置FaceDetectorProcessor并订阅以接收情感状态。然后,我们将概率转换为布尔标志。为了进行测试,让我们在布局中添加一个TextView,在其中我们将通过表情显示情绪。
全步差异
分玩
由于我们正在制作游戏,因此我们需要一个放置元素的地方。假设它以纵向模式在手机上运行。因此,让我们将屏幕分为两部分:顶部是摄像头,底部是游戏。
用微笑来控制角色非常困难,此外,黑客马拉松几乎没有时间实施高级技巧。因此,我们的角色将在比赛场地的顶部或底部一路收集nishtyak。我们将添加闭眼或睁开眼睛的动作作为游戏的复杂性:如果您闭眼捉住nishtyak,点数将加倍(
如果您想实现不同的游戏玩法,那么我可以建议一些有趣的选择:
- Guitar Hero / Just Dance-类似吉他,您需要对音乐表现出某种情感;
- 克服障碍的比赛,您需要在一定时间内到达终点或不撞车;
- 射击者,玩家眨眨眼并射击敌人。
我们将在自定义的Android视图中显示游戏-在此,在onDraw方法中,我们将在Canvas上绘制角色。在第一个原型中,我们将自己局限于几何图元。
播放器
我们的角色是正方形。在初始化期间,我们将其尺寸和位置设置在左侧,因为它就位。Y轴的位置取决于玩家的微笑。所有绝对值将相对于游戏区域的大小进行计算。这比选择特定尺寸更容易-我们将在新设备上获得可接受的外观。
private var playerSize = 0
private var playerRect = RectF()
// View
private fun initializePlayer() {
playerSize = height / 4
playerRect.left = playerSize / 2f
playerRect.right = playerRect.left + playerSize
}
//
private var flags: EmotionFlags
//
private fun movePlayer() {
playerRect.top = getObjectYTopForLine(playerSize, isTopLine = flags.isSmile).toFloat()
playerRect.bottom = playerRect.top + playerSize
}
// top size,
//
private fun getObjectYTopForLine(size: Int, isTopLine: Boolean): Int {
return if (isTopLine) {
width / 2 - width / 4 - size / 2
} else {
width / 2 + width / 4 - size / 2
}
}
// paint ,
private val playerPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
color = Color.BLUE
}
// Canvas
private fun drawPlayer(canvas: Canvas) {
canvas.drawRect(playerRect, playerPaint)
}
蛋糕
我们的角色“奔跑”并试图抓住蛋糕,以便获得尽可能多的分数。我们使用标准技术,并相对于玩家过渡到参考系统:他将静止不动,蛋糕将飞向他。如果蛋糕的平方与玩家的平方相交,则该点被计数。而且,如果同时至少有一只用户的眼睛闭着-两点\ _(ツ)_ /¯
在我们的宇宙中,也将只有一个
//
private fun initializeCake() {
cakeSize = height / 8
moveCakeToStartPoint()
}
private fun moveCakeToStartPoint() {
//
cakeRect.left = width + width * Random.nextFloat()
cakeRect.right = cakeRect.left + cakeSize
//
val isTopLine = Random.nextBoolean()
cakeRect.top = getObjectYTopForLine(cakeSize, isTopLine).toFloat()
cakeRect.bottom = cakeRect.top + cakeSize
}
//
private fun moveCake() {
val currentTime = System.currentTimeMillis()
val deltaTime = currentTime - previousTimestamp
val deltaX = cakeSpeed * width * deltaTime
cakeRect.left -= deltaX
cakeRect.right = cakeRect.left + cakeSize
previousTimestamp = currentTime
}
// ,
private fun checkPlayerCaughtCake() {
if (RectF.intersects(playerRect, cakeRect)) {
score += if (flags.isLeftEyeOpen && flags.isRightEyeOpen) 1 else 2
moveCakeToStartPoint()
}
}
// ,
private fun checkCakeIsOutOfScreenStart() {
if (cakeRect.right < 0) {
moveCakeToStartPoint()
}
}
发生了什么
让我们使点的显示非常简单。我们将在屏幕中央显示数字。您只需要考虑文字的高度,并在顶部缩进即可。
private val scorePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.GREEN
textSize = context.resources.getDimension(R.dimen.score_size)
}
private var score: Int = 0
private var scorePoint = PointF()
private fun initializeScore() {
val bounds = Rect()
scorePaint.getTextBounds("0", 0, 1, bounds)
val scoreMargin = resources.getDimension(R.dimen.score_margin)
scorePoint = PointF(width / 2f, scoreMargin + bounds.height())
score = 0
}
让我们看看我们制作了哪种玩具:
全步差异
石墨烯
为了使在骇客马拉松比赛中展示游戏不感到羞耻,让我们添加一些吧!
图片
我们从不能绘制令人印象深刻的图形这一事实出发。幸运的是,有些网站拥有免费游戏资产。我喜欢这个,尽管现在由于我未知的原因而无法直接使用。
动画
我们使用Canvas,这意味着我们需要自己实现动画。如果有带有动画的图片,将很容易对其进行编程。我们为图像变化的对象引入了一个类。
class AnimatedGameObject(
private val bitmaps: List<Bitmap>,
private val duration: Long
) {
fun getBitmap(timeInMillis: Long): Bitmap {
val mod = timeInMillis % duration
val index = (mod / duration.toFloat()) * bitmaps.size
return bitmaps[index.toInt()]
}
}
为了获得运动效果,还必须对背景进行动画处理。在内存中具有一系列背景框架是一个开销的故事。因此,让我们更巧妙地做到这一点:我们将绘制一张带有时移的图像。想法大纲:
完成步骤比较。
最后结果
很难称其为杰作,但晚上制作原型很合适。该代码可以在这里找到。在本地运行,没有其他的恶作剧。
最后,我将补充说ML Kit人脸检测在其他情况下可能会有用。
例如,与朋友进行完美的自拍照:您可以分析框架中的所有人,并确保每个人都笑着睁开眼睛。开箱即用可以检测视频流中的多个面孔,因此任务并不困难。
使用人脸检测模块中的人脸轮廓识别功能,可以复制几乎在所有相机应用中都非常流行的蒙版。如果您添加了互动功能(通过微笑和眨眼的定义),那么使用它们将会倍加有趣。
此功能-面部轮廓-可以用于娱乐之外。那些试图将照片切成文档的人会很感激的。我们采用面部轮廓,以所需的宽高比和正确的头部位置自动剪切照片。陀螺仪传感器将帮助确定正确的拍摄角度。