Android快乐贪吃蛇游戏实战项目开发教程-05虚拟方向键(四)四个三角形按钮
该系列教程概述与目录:http://www.cnblogs.com/chengyujia/p/5787111.html
一、如何判断点击的是哪个方向键按钮
在上篇教程中我们实现了左边的三角形按钮效果,本篇教程我们将左、上、右、下四个三角形按钮都一起实现了。
能做出一个来,另外三个应该不难了吧?但实际并非怎么简单哦。
首先我们来解决一下上节课遗留的一个问题,如何判断当前手指点击的是哪个三角形按钮?
这个需要用解析几何大法来解决。
假设我们的控件是边长为1的正方形,建立平面直角坐标系(注意:计算机中坐标系原点在左上角哦),如下图:
正方形的对角线将控件分成了4个三角形区域,也就是我们的4个方向键按钮。
据上图可知:
左上角到右下角对角线的方程为y=x;
y>x的区域包含左和下三角形
y<x的区域包含右和上三角形
右上角到左下角的对角线方程为y=-x+1;
y>1-x的区域包含右和下三角形
y<1-x的区域包含左和上三角形
综上可得:
y>x 且 y<1-x 表示左三角
y<x 且 y<1-x 表示上三角
y<x 且 y>1-x 表示右三角
y>x 且 y>1-x 表示下三角
以上是按照边长为1的正方形得到的结论,但实际中,我们的控件不一定是正方形,边长也不是1,而是一个不确定的矩形,这该怎么办呢?
这就需要经过一定的转换,将普通的矩形转换为边长为1的正方形。
这个转变也简单,如下:
设画布上被触摸到的点的坐标为(x,y),则:
float relativeX = x / width;//0<=relativeX<=1
float relativeY = y / height;//0<=relativeY<=1
我们将画布上被触摸的点的横纵坐标分别除以画布的宽和高,这样就得到了一个相对坐标,而这个相对坐标的取值一定在0到1之间。这样就相当于把一个不确定的矩形简化成了一个边长为1的正方形处理。
二、程序代码
有了上面的了解,下面就可以写代码了。由于有4个三角形按钮,而每个按钮又有两种状态,代码会稍微长点。但每个按钮的逻辑都是一样的,都是按哪个那个高亮,不按时都恢复正常状态。代码中的注释比较详细,相信大家如果看了前面的教程内容,看这个应该问题不大。唯一需要注意的是每次调用invalidate方法重绘界面时,是需要对整个画布都重绘的,而不能只重绘一个三角形。
这里我们需要先引入一个表示方向的枚举:Direction
package net.chengyujia.happysnake; /**
* 用来表示方向的枚举
* Created by ChengYuJia on 2016/8/21.
*/
public enum Direction {
//none表示没有方向键按下
none,
//左
left,
//上
up,
//右
right,
//下
down;
}
下面是当前DirectionKeys的完整代码:
package net.chengyujia.happysnake; import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View; /**
* 屏幕上的虚拟方向键
* Created by ChengYuJia on 2016/8/19.
*/
public class DirectionKeys extends View {
//左三角形按压时的颜色(较亮)
private int leftPressedColor = 0xFFFF0000;
//左三角形正常显示的颜色(较暗)
private int leftNormalColor = 0xFFAA0000;
//上三角形按压时的颜色(较亮)
private int upPressedColor = 0xFF00FF00;
//上三角形正常显示的颜色(较暗)
private int upNormalColor = 0xFF00AA00;
//右三角形按压时的颜色(较亮)
private int rightPressedColor = 0xFF0000FF;
//右三角形正常显示的颜色(较暗)
private int rightNormalColor = 0xFF0000AA;
//下三角形按压时的颜色(较亮)
private int downPressedColor = 0xFFFFFF00;
//下三角形正常显示的颜色(较暗)
private int downNormalColor = 0xFFAAAA00; //画笔
private Paint paint = new Paint();
//画左三角形的路径
private Path pathLeft = new Path();
//画上三角形的路径
private Path pathUp = new Path();
//画右三角形的路径
private Path pathRight = new Path();
//画下三角形的路径
private Path pathDown = new Path(); //画布的宽
private int width;
//画布的高
private int height;
//初始化方法是否执行过,确保初始化方法只执行一次。
private boolean initDone = false;
//记录当前哪个方向键被按下
private Direction currentDirection = Direction.none; //只有一个参数的构造方法是我们在程序中通过“new”关键字创建实例时调用。
public DirectionKeys(Context context) {
super(context);
} //有两个参数的构造方法是系统在XML布局文件中创建实例时调用。
public DirectionKeys(Context context, AttributeSet attrs) {
super(context, attrs);
} //初始化方法
private void init(Canvas canvas) {
/*获取画布的长和宽*/
width = canvas.getWidth();
height = canvas.getHeight();
/*
(小提示:在计算机中一般都是将左上角作为坐标原点的)
画布上四个顶点和中心点的坐标如下:
左上点 0,0
左下点 0,height
右上点 width,0
右下点 width,height
中心点 width/2,height/2
*/ /*设置左三角形的路径数据*/
//从画布左上点开始
pathLeft.moveTo(0, 0);
//画直线到画布中心点
pathLeft.lineTo(width / 2, height / 2);
//再画直线到画布左下点
pathLeft.lineTo(0, height);
//自动闭合图形。从最后一个点(左下点)画直线到第一个点(左上点)。
pathLeft.close(); /*同理设置上三角形的路径数据*/
pathUp.moveTo(0, 0);
pathUp.lineTo(width / 2, height / 2);
pathUp.lineTo(width, 0);
pathUp.close(); /*同理设置右三角形的路径数据*/
pathRight.moveTo(width, 0);
pathRight.lineTo(width / 2, height / 2);
pathRight.lineTo(width, height);
pathRight.close(); /*同理设置下三角形的路径数据*/
pathDown.moveTo(width, height);
pathDown.lineTo(width / 2, height / 2);
pathDown.lineTo(0, height);
pathDown.close();
} //画路径的共用方法
private void drawPath(Path path, int color, Canvas canvas) {
//设置画笔颜色
paint.setColor(color);
//用画笔在画布上按照路径数据画出图形
canvas.drawPath(path, paint);
} //画左三角正常颜色
private void drawLeftNormal(Canvas canvas) {
drawPath(pathLeft, leftNormalColor, canvas);
} //画左三角按压颜色(高亮)
private void drawLeftPressed(Canvas canvas) {
drawPath(pathLeft, leftPressedColor, canvas);
} //画上三角正常颜色
private void drawUpNormal(Canvas canvas) {
drawPath(pathUp, upNormalColor, canvas);
} //画上三角按压颜色(高亮)
private void drawUpPressed(Canvas canvas) {
drawPath(pathUp, upPressedColor, canvas);
} //画右三角正常颜色
private void drawRightNormal(Canvas canvas) {
drawPath(pathRight, rightNormalColor, canvas);
} //画右三角按压颜色(高亮)
private void drawRightPressed(Canvas canvas) {
drawPath(pathRight, rightPressedColor, canvas);
} //画下三角正常颜色
private void drawDownNormal(Canvas canvas) {
drawPath(pathDown, downNormalColor, canvas);
} //画下三角按压颜色(高亮)
private void drawDownPressed(Canvas canvas) {
drawPath(pathDown, downPressedColor, canvas);
} //所有按钮恢复正常颜色
private void reset(Canvas canvas) {
drawLeftNormal(canvas);
drawUpNormal(canvas);
drawRightNormal(canvas);
drawDownNormal(canvas);
} //当按左三角时,左三角高亮,其它正常。
private void drawWhenLeftPressed(Canvas canvas) {
drawLeftPressed(canvas);
drawUpNormal(canvas);
drawRightNormal(canvas);
drawDownNormal(canvas);
} //当按上三角时,上三角高亮,其它正常。
private void drawWhenUpPressed(Canvas canvas) {
drawLeftNormal(canvas);
drawUpPressed(canvas);
drawRightNormal(canvas);
drawDownNormal(canvas);
} //当按右三角时,右三角高亮,其它正常。
private void drawWhenRightPressed(Canvas canvas) {
drawLeftNormal(canvas);
drawUpNormal(canvas);
drawRightPressed(canvas);
drawDownNormal(canvas);
} //当按下三角时,下三角高亮,其它正常。
private void drawWhenDownPressed(Canvas canvas) {
drawLeftNormal(canvas);
drawUpNormal(canvas);
drawRightNormal(canvas);
drawDownPressed(canvas);
} /**
* 通过重写父类的onDraw方法来绘制我们需要的图形
* 该方法会在控件第一次显示时被系统调用,并在之后每次调用invalidate方法后被系统调用。
*
* @param canvas 这里的canvas是系统提供的一块矩形画布,我们要做的就是在这块画布上画我们想要的东西。
*/
@Override
protected void onDraw(Canvas canvas) {
if (!initDone) {
init(canvas);
//确保初始化方法只执行一次
initDone = true;
} //按下不同的方向键,绘制不同的界面效果。
switch (currentDirection) {
case left://按下左键时
drawWhenLeftPressed(canvas);
break;
case up://按下上键时
drawWhenUpPressed(canvas);
break;
case right://按下右键时
drawWhenRightPressed(canvas);
break;
case down://按下下键时
drawWhenDownPressed(canvas);
break;
default://其它情况
reset(canvas);
}
} /**
* 当用户触摸到该控件时,系统通过该方法告诉控件“你被摸了,要不要有反应啊?有反应返回true,没反应返回false。”
* 控件说“那要看是怎么摸的了。如果是按下,我就将对应的三角形按键高亮显示;如果是抬起,我就将所有的三角形按键恢复成正常的颜色。其它情况我就不反应了,摸就摸吧。”
*
* @param event 系统给我们传递的触摸事件参数
* @return 如果该触摸事件被我们处理了返回true,反之返回false。
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
//获取当前的触摸动作
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {//按下
//获取触摸点的坐标
float x = event.getX();
float y = event.getY();
currentDirection = getDirection(x, y);
invalidate();//重绘
return true;
} else if (action == MotionEvent.ACTION_UP) {//抬起
currentDirection = Direction.none;
invalidate();//重绘
return true;
} else {//其它不处理
return false;
}
} //根据坐标判断哪个三角形方向键被按下
private Direction getDirection(float x, float y) {
//经过坐标转换,统一成边长为1的正方形处理。对角线分割形成的4个区域,分别代表4个方向。
float relativeX = x / width;//0<=relativeX<=1
float relativeY = y / height;//0<=relativeY<=1
/*
注意:原点是左上角。
左上角到右下角对角线方程为y=x;
则:
y>x的区域包含左和下三角形
y<x的区域包含右和上三角形 左下角到右上角对角线方程为y=-x+1;
则:
y>1-x的区域包含右和下三角形
y<1-x的区域包含左和上三角形 综上可得:
y>x 且 y<1-x 表示左三角
y<x 且 y<1-x 表示上三角
y<x 且 y>1-x 表示右三角
y>x 且 y>1-x 表示下三角
*/ if (relativeY > relativeX) {//左和下
if (relativeY < 1 - relativeX) {//左
return Direction.left;
} else {//下
return Direction.down;
}
} else {//上和右
if (relativeY < 1 - relativeX) {//上
return Direction.up;
} else {//右
return Direction.right;
}
}
}
}
三、运行效果
没有点击时的效果:

点击左键时的效果:

点击上建时的效果:

点击右键时的效果:

点击下键时的效果:

到这里,我们的自定义方向键的4个背景三角形已经做好了,而且实现了点击变色的按钮效果。
后面我们会在这4个三角形上都画上相应的箭头,同样也有点击变色的效果哦。敬请期待o(^▽^)o
Android快乐贪吃蛇游戏实战项目开发教程-05虚拟方向键(四)四个三角形按钮的更多相关文章
- Android快乐贪吃蛇游戏实战项目开发教程-04虚拟方向键(三)三角形按钮效果
该系列教程概述与目录:http://www.cnblogs.com/chengyujia/p/5787111.html 一.知识点讲解 当我们点击系统自带的按钮时,按钮的外观会发生变化.上篇博文中我们 ...
- Android快乐贪吃蛇游戏实战项目开发教程-02虚拟方向键(一)自定义控件概述
该系列教程概述与目录:http://www.cnblogs.com/chengyujia/p/5787111.html 一.自定义控件简介 在本项目中,无论是游戏主区域还是虚拟方向键都是通过自定义控件 ...
- Android快乐贪吃蛇游戏实战项目开发教程-06虚拟方向键(五)绘制方向键箭头
本系列教程概述与目录:http://www.cnblogs.com/chengyujia/p/5787111.html本系列教程项目源码GitHub地址:https://github.com/jack ...
- Android快乐贪吃蛇游戏实战项目开发教程-03虚拟方向键(二)绘制一个三角形
该系列教程概述与目录:http://www.cnblogs.com/chengyujia/p/5787111.html 一.绘制三角形 在上一篇文章中,我们已经新建了虚拟方向键的自定义控件Direct ...
- Android快乐贪吃蛇游戏实战项目开发教程-01项目概述与目录
一.项目简介 贪吃蛇是一个很经典的游戏,也很适合用来学习.本教程将和大家一起做一个Android版的贪吃蛇游戏. 我已经将做好的案例上传到了应用宝,无病毒.无广告,大家可以放心下载下来把玩一下.应用宝 ...
- 原生Js贪吃蛇游戏实战开发笔记
前言 本课程是通过JavaScript结合WebAPI DOM实现的一版网页游戏---贪吃蛇的开发全过程,采用面向以象的思想设计开发.通过这个小游戏的开发, 不仅可以掌握JS的语法的应用,还可以学会D ...
- Android实现贪吃蛇游戏
[绥江一百]http://www.sj100.net 欢迎,进入绥江一百感谢点击[我的小网站,请大家多 ...
- 【C语言项目】贪吃蛇游戏(上)
目录 00. 目录 01. 开发背景 02. 功能介绍 03. 欢迎界面设计 3.1 常用终端控制函数 3.2 设置文本颜色函数 3.3 设置光标位置函数 3.4 绘制字符画(蛇) 3.5 欢迎界面函 ...
- 使用Love2D引擎开发贪吃蛇游戏
今天来介绍博主近期捣腾的一个小游戏[贪吃蛇],贪吃蛇这个游戏相信大家都不会感到陌生吧.今天博主将通过Love2D这款游戏引擎来为大家实现一个简单的贪吃蛇游戏,在本篇文章其中我们将会涉及到贪吃蛇的基本算 ...
随机推荐
- 谈谈如何使用Netty开发实现高性能的RPC服务器
RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议.说的再直白一点,就是客户端在不必知道 ...
- Hello Web API系列教程——Web API与国际化
软件国际化是在软件设计和文档开发过程中,使得功能和代码设计能处理多种语言和文化习俗,在创建不同语言版本时,不需要重新设计源程序代码的软件工程方法.这在很多成熟的软件开发平台中非常常见.对于.net开发 ...
- div实现自适应高度的textarea,实现angular双向绑定
相信不少同学模拟过腾讯的QQ做一个聊天应用,至少我是其中一个. 过程中我遇到的一个问题就是QQ输入框,自适应高度,最高高度为3row. 如果你也像我一样打算使用textarea,那么很抱歉,你一开始就 ...
- Ngrok让你的本地Web应用暴露在公网上
1.Ngrok介绍 Ngrok是一个反向代理,通过在公共的端点和本地运行的Web服务器之间建立一个安全的通道.Ngrok可捕获和分析所有通道上的流量,便于后期分析和重放.简单来说,利用 Ngrok可以 ...
- HTML文档声明
前面的话 HTML文档通常以类型声明开始,该声明将帮助浏览器确定其尝试解析和显示的HTML文档类型.本文将详细介绍文档声明DOCTYPE 特点 文档声明必须是HTML文档的第一行.且顶格显示, ...
- [C#][算法] 用菜鸟的思维学习算法 -- 马桶排序、冒泡排序和快速排序
用菜鸟的思维学习算法 -- 马桶排序.冒泡排序和快速排序 [博主]反骨仔 [来源]http://www.cnblogs.com/liqingwen/p/4994261.html 目录 马桶排序(令人 ...
- 用原生js做单页应用
最近在公司接到一个需求,里面有一个三级跳转.类似于选择地址的时候,选择的顺序是:省份->市->区.如果分三个页面跳转,那么体验非常不好,如果引入其他框架做成单页应用,又比较麻烦.所以可以用 ...
- 代码的坏味道(19)——狎昵关系(Inappropriate Intimacy)
坏味道--狎昵关系(Inappropriate Intimacy) 特征 一个类大量使用另一个类的内部字段和方法. 问题原因 类和类之间应该尽量少的感知彼此(减少耦合).这样的类更容易维护和复用. 解 ...
- 使用po模式读取豆瓣读书最受关注的书籍,取出标题、评分、评论、题材 按评分从小到大排序并输出到txt文件中
#coding=utf-8from time import sleepimport unittestfrom selenium import webdriverfrom selenium.webdri ...
- PHP设计模式(六)原型模式(Prototype For PHP)
原型设计模式: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型设计模式简单的来说,顾名思义, 不去创建新的对象进而保留原型的一种设计模式. 缺点:原型设计模式是的最主要的缺点就 ...