| 赞 | 10 |
| VIP | |
| 好人卡 | |
| 积分 | 4 |
| 经验 | |
| 最后登录 | 2022-3-2 |
| 在线时间 | 48 小时 |
- 梦石
- 0
- 星屑
- 390
- 在线时间
- 48 小时
- 注册时间
- 2020-4-24
- 回帖
- 55
|
加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有账号?注册会员
×
本帖最后由 haiyin 于 2021-11-20 22:24 编辑
更新了简洁版
https://rpg.blue/forum.php?mod=viewthread&tid=487798&page=1&extra=#pid2936081
如果有android代码基础的话,我更推荐本篇的版本。
RPGMaker官方引擎制作的只有四个方向,这点对应使用摇杆操作体验还是有微小影响,思来想去,我最后只能是将左下、右下方向都指定为下方向,左上、右上都指定为上方向
前面发了贴请教关于存档问题,自己也去查了,结果还是无法突破 ...不过倒是对javascript和webview交互有了一点了解。
之前就想做个虚拟按键,可自己对javascript了解不深,就开始学习,前几天跟着大佬xjzsq(https://rpg.blue/?2631080)的教程学习完。非常感谢!!!由此产生此篇。
第一次写教程,有不妥的地方请尽情提建议。
有了点这些基础,我就想直接用android层做虚拟键,只要去调用javascript事件就行了!然后我主要用得是java语言,编译器是AS,所以需要一点android开发基础...
如果完全不懂android开发估计不好看明白,但你也可以尝试一下,多一门手艺也不是坏事...
上效果
下面说教程吧:
1、解压后 把文件添加到包内(原作者下载地址(应该?我发现了很多相似的O(∩_∩)O哈哈~)
https://download.csdn.net/download/weixin_39840650/11489681?utm_source=bbsseo
这个主要是绘制摇杆的自定义组件
2、将解压后的attr文件复制到value文件夹中,这个主要是用来设置摇杆样式的,如果有P好的摇杆图可以在这设置,然后在布局中调用,如果没有图片,那么就会参考颜色值来绘制摇杆
3、在布局文件中调用,就是在你创建webview组件的那个文件里创建摇杆view类,至于如何布局应该不用我说了吧
4、在MainActivity中添加代码 实例化view什么的大家应该都会,这里就展示需要添加的部分
[pre lang="java" file="MainActivity"]
mRockerView.setOnShakeListener(DIRECTION_8, new RockerViewtwo.OnShakeListener() {
@Override
public void onStart() {
}
@Override
public void direction(RockerViewtwo.Direction direction) {
if (direction == RockerViewtwo.Direction.DIRECTION_CENTER) {
Log.e("_now9", "当前方向:中心");
goiuout();
} else if (direction == RockerViewtwo.Direction.DIRECTION_DOWN) {
Log.e("_now8", "当前方向:下");
goiuout();
goiudown();
} else if (direction == RockerViewtwo.Direction.DIRECTION_LEFT) {
Log.e("_now7", "当前方向:左");
goiuout();
goiuleft();
} else if (direction == RockerViewtwo.Direction.DIRECTION_UP) {
Log.e("_now6", "当前方向:上");
goiuout();
goiuup();
} else if (direction == RockerViewtwo.Direction.DIRECTION_RIGHT) {
Log.e("_now5", "当前方向:右");
goiuout();
goiuright();
} else if (direction == RockerViewtwo.Direction.DIRECTION_DOWN_LEFT) {
Log.e("_now4", "当前方向:左下");
// goiuleft();
goiuout();
goiudown();
} else if (direction == RockerViewtwo.Direction.DIRECTION_DOWN_RIGHT) {
Log.e("_now3", "当前方向:右下");
// goiuright();
goiuout();
goiudown();
} else if (direction == RockerViewtwo.Direction.DIRECTION_UP_LEFT) {
Log.e("_now2", "当前方向:左上");
// goiuleft();
goiuout();
goiuup();
} else if (direction == RockerViewtwo.Direction.DIRECTION_UP_RIGHT) {
Log.e("_now1", "当前方向:右上");
// goiuright();
goiuout();
goiuup();
}
}
@Override
public void onFinish() {
}
});
mRockerView.setOnAngleChangeListener(new RockerViewtwo.OnAngleChangeListener() {
@Override
public void onStart() {
}
@Override
public void angle(double angle) {
Log.e("_now10", "当前角度:" + angle);
}
@Override
public void onFinish() {
}
});
mRockerView.setOnDistanceLevelListener(new RockerViewtwo.OnDistanceLevelListener() {
@Override
public void onDistanceLevel(int level) {
Log.e("_now11", "当前距离级别:" + level);
}
});
ublic static void goiuout() {
gameWebView.loadUrl("javascript:downgetout()");
}
public static void goiuleft() {
gameWebView.loadUrl("javascript:downleft()");
}
public static void goiuup() {
gameWebView.loadUrl("javascript:downup()");
}
public static void goiudown() {
gameWebView.loadUrl("javascript:downdrow()");
}
public static void goiuright() {
gameWebView.loadUrl("javascript:downright()");
}
public static void goiuok() {
gameWebView.loadUrl("javascript:downok()");
}
public static void goiuesc() {
gameWebView.loadUrl("javascript:downesc()");
}[/pre]
5、既然是交互,下面就是在javascript上操作了
打开游戏文件index.html文件,在body标签内、插件引用最后处添加下面代码
[pre lang="javascript" file="index.html"]
<script type="text/javascript">
function downleft(){
Input._currentState['left']=true
}
function downright(){
Input._currentState['right']=true
}
function downup(){
Input._currentState['up']=true
}
function downdrow(){
Input._currentState['down']=true
}
function downok(){
Input._currentState['ok']=true
}
function downesc(){
Input._currentState['escape']=true
}
function downgetout(){
Input._currentState['up']=false
Input._currentState['right']=false
Input._currentState['down']=false
Input._currentState['left']=false
Input._currentState['ok']=false
Input._currentState['escape']=false
}
</script>[/pre]
补全一下MainActivity.java吧,方便对上面第四条出现的代码进行查询:
MainActivity.java:上面第四条是该文件的节选,其实摇杆核心就是第四条的代码
[pre lang="java" file="MainActivity"]package com.example.myhtmltwo;
import static com.example.myhtmltwo.RockerViewtwo.DirectionMode.DIRECTION_8;
import android.Manifest;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.app.ActivityCompat;
import androidx.webkit.WebViewAssetLoader;
import java.io.InputStream;
/**
* @author HY
*/
public class MainActivity extends AppCompatActivity {
/**
* 动画控件imgview
*/
LinearLayout ani;
TextView loadtxt;
RockerViewtwo mRockerView;
/**
* game路径
*/
String gameUrl;
/**
* 承载网页控件
*/
public static WebView gameWebView;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* 去除标题
*/
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
/**
* 去掉信息栏
*/
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
showCamera();
Bundle bundle = this.getIntent().getExtras();
gameUrl = bundle.getString("gamePath");
/**
* 实例化动画控件
*/
ani = findViewById(R.id.imgview);
loadtxt = findViewById(R.id.loadtxt);
/**
* 加载网页
*/
htmls();
/**
* 动画
*/
loadAmi();
}
public void showCamera() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (!Environment.isExternalStorageManager()) {
androidx.appcompat.app.AlertDialog.Builder ad = new androidx.appcompat.app.AlertDialog.Builder(MainActivity.this);
ad.setMessage("由于Android11及以上系统限制需要对软件进行授权,请开启权限")
.setCancelable(false)
.setPositiveButton("前往开启", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.setData(Uri.parse("package:" + MainActivity.this.getPackageName()));
startActivityForResult(intent, 98);
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
}).show();
}
}
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
ActivityCompat.requestPermissions(MainActivity.this, permissions, 520);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 520:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.e("申请:", "成功!!");
} else {
finish();
}
break;
}
}
/**
* 加载网页
*/
RemoteSurfaceView remoteSurfaceView;
RelativeLayout.LayoutParams params;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public void htmls() {
/**
* 实例化WebView
*/
mRockerView = (RockerViewtwo) findViewById(R.id.roview);
Button gsq = findViewById(R.id.gsq);
Button gsq2 = findViewById(R.id.gsq2);
gameWebView = findViewById(R.id.gameWebView);
Log.e("_now", "当前模式setOnShakeListener:方向有改变时回调;8个方向");
mRockerView.setOnShakeListener(DIRECTION_8, new RockerViewtwo.OnShakeListener() {
@Override
public void onStart() {
}
@Override
public void direction(RockerViewtwo.Direction direction) {
if (direction == RockerViewtwo.Direction.DIRECTION_CENTER) {
Log.e("_now9", "当前方向:中心");
goiuout();
} else if (direction == RockerViewtwo.Direction.DIRECTION_DOWN) {
Log.e("_now8", "当前方向:下");
goiuout();
goiudown();
} else if (direction == RockerViewtwo.Direction.DIRECTION_LEFT) {
Log.e("_now7", "当前方向:左");
goiuout();
goiuleft();
} else if (direction == RockerViewtwo.Direction.DIRECTION_UP) {
Log.e("_now6", "当前方向:上");
goiuout();
goiuup();
} else if (direction == RockerViewtwo.Direction.DIRECTION_RIGHT) {
Log.e("_now5", "当前方向:右");
goiuout();
goiuright();
} else if (direction == RockerViewtwo.Direction.DIRECTION_DOWN_LEFT) {
Log.e("_now4", "当前方向:左下");
// goiuleft();
goiuout();
goiudown();
} else if (direction == RockerViewtwo.Direction.DIRECTION_DOWN_RIGHT) {
Log.e("_now3", "当前方向:右下");
// goiuright();
goiuout();
goiudown();
} else if (direction == RockerViewtwo.Direction.DIRECTION_UP_LEFT) {
Log.e("_now2", "当前方向:左上");
// goiuleft();
goiuout();
goiuup();
} else if (direction == RockerViewtwo.Direction.DIRECTION_UP_RIGHT) {
Log.e("_now1", "当前方向:右上");
// goiuright();
goiuout();
goiuup();
}
}
@Override
public void onFinish() {
}
});
mRockerView.setOnAngleChangeListener(new RockerViewtwo.OnAngleChangeListener() {
@Override
public void onStart() {
}
@Override
public void angle(double angle) {
Log.e("_now10", "当前角度:" + angle);
}
@Override
public void onFinish() {
}
});
mRockerView.setOnDistanceLevelListener(new RockerViewtwo.OnDistanceLevelListener() {
@Override
public void onDistanceLevel(int level) {
Log.e("_now11", "当前距离级别:" + level);
}
});
Resources resources = MainActivity.this.getResources();
Drawable qxrd = resources.getDrawable(R.drawable.qxrd);
Drawable qx = resources.getDrawable(R.drawable.qx);
Drawable qdrd = resources.getDrawable(R.drawable.qdrd);
Drawable qd = resources.getDrawable(R.drawable.qd);
gsq.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
gsq.setBackground(qxrd);
gameWebView.loadUrl("javascript:downesc()");
} else if (action == MotionEvent.ACTION_UP) {
gsq.setBackground(qx);
gameWebView.loadUrl("javascript:downgetout()");
}
return false;
}
});
gsq2.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
gsq2.setBackground(qdrd);
gameWebView.loadUrl("javascript:downok()");
} else if (action == MotionEvent.ACTION_UP) {
gsq2.setBackground(qd);
gameWebView.loadUrl("javascript:downgetout()");
}
return false;
}
});
// gsq2.setOnClickListener(v -> {
// gameWebView.loadUrl("javascript:downok()");
// });
/**
* 清除缓存
*/
gameWebView.clearCache(true);
/**
* 实例化WebView配置管理类
*/
WebSettings settings = gameWebView.getSettings();
/**
* 解决跨域
*/
settings.setAllowFileAccessFromFileURLs(true);
/**
*启用Dom存储
*/
settings.setDomStorageEnabled(true);
/**
*允许加载js文件
*/
settings.setJavaScriptEnabled(true);
/**
* 支持缩放
*/
settings.setSupportZoom(true);
/**
* 设置横屏
*/
// setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
/**
* 页面事件响应
*/
gameWebView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
if (newProgress == 100) {
// loadtxt.setVisibility(View.GONE);
// ani.setVisibility(View.GONE);
} else {
loadtxt.setVisibility(View.VISIBLE);
ani.setVisibility(View.VISIBLE);
}
}
});
/**
* 判断SDK版本
* SDK版本为21才可使用方法WebViewAssetLoader
* 否则
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
/**
* html项目存放路径,一般是assets
*/
.addPathHandler("/assets/", new WebViewAssetLoader.AssetsPathHandler(this))
/**
* html引用的资源文件路径 ,一般是跟随html存放于assets
*/
.addPathHandler("/assets/", new WebViewAssetLoader.ResourcesPathHandler(this))
.build();
/**
*与页面的交互
*/
gameWebView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
loadtxt.setVisibility(View.GONE);
ani.setVisibility(View.GONE);
super.onPageFinished(view, url);
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
return assetLoader.shouldInterceptRequest(request.getUrl());
}
});
gameWebView.addJavascriptInterface(new AndroidtoJs(this), "JH");
gameWebView.loadUrl("https://appassets.androidplatform.net/assets/" + gameUrl);
} else {
gameWebView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
loadtxt.setVisibility(View.GONE);
ani.setVisibility(View.GONE);
super.onPageFinished(view, url);
}
});
gameWebView.loadUrl("file:///android_asset/" + gameUrl);
}
}
public static void goiuout() {
gameWebView.loadUrl("javascript:downgetout()");
}
public static void goiuleft() {
gameWebView.loadUrl("javascript:downleft()");
}
public static void goiuup() {
gameWebView.loadUrl("javascript:downup()");
}
public static void goiudown() {
gameWebView.loadUrl("javascript:downdrow()");
}
public static void goiuright() {
gameWebView.loadUrl("javascript:downright()");
}
public static void goiuok() {
gameWebView.loadUrl("javascript:downok()");
}
public static void goiuesc() {
gameWebView.loadUrl("javascript:downesc()");
}
private ImageView myImageView, myImageView2, myImageView3, myImageView4;
private Handler myHandler;//定义线程运行标识
private Boolean iswalking = true;
class myThread extends Thread {
@Override
public void run() {
int what = 1;
while (iswalking) {
myHandler.sendEmptyMessage((what++) % 4);//每次生成0-3间依次增加的的数字,用来充当图片数组的下标。
try {
Thread.sleep(250);//每250毫秒睡眠一次
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public Bitmap BitmapClipBitmap(Bitmap bitmap, int x, int y, int w, int h) {
return Bitmap.createBitmap(bitmap, x, y, w, h);
}
public Bitmap ReadBitmap(Context context, int resId) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
//图片的输入流
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}
/**
* 加载动画
*/
public void loadAmi() {
final int ANIM_COUNT_X = 4, ANIM_COUNT_Y = 4, ANIM_DOWN = 0, ANIM_LEFT = 1, ANIM_RIGHT = 2, ANIM_UP = 3;
Context context = getApplicationContext();//获取Context
Bitmap testmap = ReadBitmap(context, R.drawable.girl);
final Bitmap[][] bitmap = new Bitmap[ANIM_COUNT_X][ANIM_COUNT_Y]; //建立4x4二维数组用来存储4个角度的图片
int tileWidth = testmap.getWidth() / ANIM_COUNT_X;
int tileHeight = testmap.getHeight() / ANIM_COUNT_Y;
int i = 0, x = 0, y = 0;
for (i = 0; i < ANIM_COUNT_Y; i++) {
y = 0;
bitmap[ANIM_DOWN] = BitmapClipBitmap(testmap, x, y, tileWidth, tileHeight);
y += tileHeight;
bitmap[ANIM_LEFT] = BitmapClipBitmap(testmap, x, y, tileWidth, tileHeight);
y += tileHeight;
bitmap[ANIM_RIGHT] = BitmapClipBitmap(testmap, x, y, tileWidth, tileHeight);
y += tileHeight;
bitmap[ANIM_UP] = BitmapClipBitmap(testmap, x, y, tileWidth, tileHeight);
x += tileWidth;
// setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
//设置屏幕为横向
myImageView = (ImageView) findViewById(R.id.im1);
myImageView2 = (ImageView) findViewById(R.id.im2);
myImageView3 = (ImageView) findViewById(R.id.im3);
myImageView4 = (ImageView) findViewById(R.id.im4);
myHandler = new Handler() {//实例化Handler对象
@Override
public void handleMessage(Message msg) {//接收消息
super.handleMessage(msg);
myImageView.setImageBitmap(bitmap[ANIM_UP][msg.what]);//设置ImageView中所要加载的位图
myImageView2.setImageBitmap(bitmap[ANIM_UP][msg.what]);
myImageView3.setImageBitmap(bitmap[ANIM_RIGHT][msg.what]);
myImageView4.setImageBitmap(bitmap[ANIM_RIGHT][msg.what]);
}
};
new myThread().start();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
/**
* 退出
*/
public void exit() {
finish();//退出,但是进程仍在
android.os.Process.killProcess(android.os.Process.myPid());
}
/**
* 返回键按下时退出
*
* @param keyCode
* @param event
* @return
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("是否退出应用?");
builder.setTitle("退出");
/**
* setIcon方法
* 必须设置了setTitleicon才有效
*/
builder.setIcon(R.mipmap.ic_launcher);
builder.setPositiveButton("退出", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
exit();
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
}
});
builder.show();
return true;
}
return super.onKeyDown(keyCode, event);
}
}
[/pre]
确定和取消就是两个按钮加上图片,然后调用javascript内的方法即可
到此,算是完成了吧
最后再次感谢用爱发电出教程的各位大佬,感谢感谢!!!
data.zip
(79.93 KB, 下载次数: 6, 售价: 10 星屑)
然后就是国内android厂家各种对android进行自己的定制,保不准哪些手机可能会有bug什么的,我在vivo测试是没问题的,欢迎测试。
最后的最后我把自己的工程文件和构筑好的apk分享下,如果能帮到你是最好的。
项目文件:
链接:https://pan.baidu.com/s/1Xmp3cfbPnkm7DZsqrc2_MQ
提取码:5ppu
--来自百度网盘超级会员V4的分享
APK:
链接:https://pan.baidu.com/s/1r_Gc0bUuLXRstEI626Yc3A
提取码:g0an
--来自百度网盘超级会员V4的分享
|
评分
-
查看全部评分
|