├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── dictionaries │ └── Liao.xml ├── encodings.xml ├── gradle.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── kuoruan │ │ └── bomberman │ │ └── ExampleInstrumentationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── kuoruan │ │ │ └── bomberman │ │ │ ├── activity │ │ │ ├── BaseActivity.java │ │ │ ├── ConnectionActivity.java │ │ │ ├── GameActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── SplashActivity.java │ │ │ └── WifiGameActivity.java │ │ │ ├── adapter │ │ │ ├── ChatAdapter.java │ │ │ └── ConnectionAdapter.java │ │ │ ├── dao │ │ │ ├── GameDataDao.java │ │ │ ├── GameLevelDataDao.java │ │ │ └── GameOpenHelper.java │ │ │ ├── entity │ │ │ ├── BaseImage.java │ │ │ ├── Bomb.java │ │ │ ├── BombFire.java │ │ │ ├── ChatContent.java │ │ │ ├── Collision.java │ │ │ ├── ConnectionItem.java │ │ │ ├── DynamicImage.java │ │ │ ├── GameTile.java │ │ │ ├── GameUi.java │ │ │ ├── Player.java │ │ │ └── data │ │ │ │ ├── GameData.java │ │ │ │ └── GameLevelData.java │ │ │ ├── net │ │ │ ├── ConnectedService.java │ │ │ └── ConnectingService.java │ │ │ ├── util │ │ │ ├── BitmapManager.java │ │ │ ├── ConnectConstants.java │ │ │ ├── DataUtil.java │ │ │ ├── GameConstants.java │ │ │ ├── PlayerManager.java │ │ │ ├── SPUtils.java │ │ │ └── SceneManager.java │ │ │ └── view │ │ │ └── GameView.java │ └── res │ │ ├── drawable-hdpi │ │ ├── background.png │ │ ├── bomb_1.png │ │ ├── bomb_1_0.png │ │ ├── bomb_1_1.png │ │ ├── btn_circle_style.xml │ │ ├── btn_white_style.xml │ │ ├── ctrl_down_arrow.png │ │ ├── ctrl_left_arrow.png │ │ ├── ctrl_right_arrow.png │ │ ├── ctrl_up_arrow.png │ │ ├── fire.png │ │ ├── game_bg.png │ │ ├── player_1.png │ │ ├── player_unit.png │ │ ├── set_bomb.png │ │ ├── tile_01.png │ │ ├── tile_02.png │ │ └── tile_03.png │ │ ├── drawable-mdpi │ │ ├── bomb_1.png │ │ ├── bomb_1_0.png │ │ ├── bomb_1_1.png │ │ ├── ctrl_down_arrow.png │ │ ├── ctrl_left_arrow.png │ │ ├── ctrl_right_arrow.png │ │ ├── ctrl_up_arrow.png │ │ ├── fire.png │ │ ├── game_bg.png │ │ ├── player_1.png │ │ ├── player_unit.png │ │ ├── set_bomb.png │ │ ├── tile_01.png │ │ ├── tile_02.png │ │ └── tile_03.png │ │ ├── layout │ │ ├── activity_connection.xml │ │ ├── activity_main.xml │ │ ├── chat_dialog.xml │ │ ├── chat_item.xml │ │ └── connection_item.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ ├── values-zh-rCN │ │ └── strings.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── kuoruan │ └── bomberman │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/dictionaries/Liao.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | 27 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 54 | 55 | 56 | 57 | 58 | Android API 19 Platform 59 | 60 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "24" 6 | defaultConfig { 7 | applicationId "com.kuoruan.bomberman" 8 | minSdkVersion 15 9 | targetSdkVersion 23 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile fileTree(include: ['*.jar'], dir: 'libs') 23 | compile 'com.android.support:appcompat-v7:24.0.0' 24 | } 25 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Android\Android-SDK/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/kuoruan/bomberman/ExampleInstrumentationTest.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.filters.MediumTest; 6 | import android.support.test.runner.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | 12 | import static org.junit.Assert.*; 13 | 14 | /** 15 | * Instrumentation test, which will execute on an Android device. 16 | * 17 | * @see Testing documentation 18 | */ 19 | @MediumTest 20 | @RunWith(AndroidJUnit4.class) 21 | public class ExampleInstrumentationTest { 22 | @Test 23 | public void useAppContext() throws Exception { 24 | // Context of the app under test. 25 | Context appContext = InstrumentationRegistry.getTargetContext(); 26 | 27 | assertEquals("com.kuoruan.bomberman", appContext.getPackageName()); 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 28 | 29 | 34 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/activity/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.activity; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.support.v7.app.ActionBar; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.Gravity; 8 | import android.view.KeyEvent; 9 | import android.view.LayoutInflater; 10 | import android.view.MenuItem; 11 | import android.view.View; 12 | 13 | /** 14 | * Created by Liao on 2016/6/16 0016. 15 | */ 16 | 17 | public abstract class BaseActivity extends AppCompatActivity { 18 | 19 | protected String TAG = this.getClass().getSimpleName(); 20 | private ActionBar mActionBar; 21 | protected LayoutInflater mInflater; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | onBeforeSetContentView(); 27 | if (getLayoutId() != 0) { 28 | setContentView(getLayoutId()); 29 | } 30 | mInflater = getLayoutInflater(); 31 | 32 | if (hasActionBar()) { 33 | initActionBar(); 34 | } 35 | init(savedInstanceState); 36 | } 37 | 38 | protected void initActionBar() { 39 | mActionBar = getSupportActionBar(); 40 | if(mActionBar!=null) { 41 | //mActionBar.setBackgroundDrawable(getResources().getDrawable(R.color.actionbar_bg)); 42 | mActionBar.setDisplayShowTitleEnabled(true); 43 | mActionBar.setDisplayUseLogoEnabled(true); 44 | if (hasBackButton()) { 45 | mActionBar.setDisplayShowHomeEnabled(false); 46 | mActionBar.setDisplayHomeAsUpEnabled(true); 47 | //mActionBar.setHomeAsUpIndicator(R.drawable.ic_launcher); 48 | } 49 | if (hasActionBarCustomView()) { 50 | mActionBar.setDisplayShowCustomEnabled(true); 51 | int layoutRes = getActionBarCustomViewLayoutRescId(); 52 | View actionBarView = inflateView(layoutRes); 53 | ActionBar.LayoutParams params; 54 | if (isAllActionBarCustom()) { 55 | params = new ActionBar.LayoutParams(ActionBar.LayoutParams.MATCH_PARENT, 56 | ActionBar.LayoutParams.MATCH_PARENT); 57 | } else { 58 | params = new ActionBar.LayoutParams(ActionBar.LayoutParams.WRAP_CONTENT, 59 | ActionBar.LayoutParams.MATCH_PARENT); 60 | params.gravity = Gravity.RIGHT; 61 | } 62 | mActionBar.setCustomView(actionBarView, params); 63 | handlerActionBarCustomViewAction(actionBarView); 64 | } 65 | } 66 | } 67 | 68 | @Override 69 | public boolean onOptionsItemSelected(MenuItem item) { 70 | switch (item.getItemId()) { 71 | case android.R.id.home: 72 | onBackAction(); 73 | break; 74 | default: 75 | break; 76 | } 77 | return super.onOptionsItemSelected(item); 78 | } 79 | 80 | private void onBackAction() { 81 | onBackPressed(); 82 | } 83 | 84 | protected void handlerActionBarCustomViewAction(View actionBarView) { 85 | 86 | } 87 | 88 | private View inflateView(int layoutRes) { 89 | return mInflater.inflate(layoutRes, null); 90 | } 91 | 92 | protected boolean hasActionBarCustomView() { 93 | return false; 94 | } 95 | 96 | protected boolean hasBackButton() { 97 | return true; 98 | } 99 | 100 | protected boolean hasActionBar() { 101 | return true; 102 | } 103 | 104 | protected void onBeforeSetContentView() { 105 | } 106 | 107 | 108 | protected abstract int getLayoutId(); 109 | 110 | protected abstract void init(Bundle savedInstanceState); 111 | 112 | protected int getActionBarCustomViewLayoutRescId() { 113 | return 0; 114 | } 115 | 116 | protected boolean isAllActionBarCustom() { 117 | return false; 118 | } 119 | 120 | protected void setActionBarTitle(int resId) { 121 | if (resId != 0) { 122 | setActionBarTitle(getString(resId)); 123 | } 124 | } 125 | 126 | protected void setActionBarTitle(String title) { 127 | if (hasActionBar()) { 128 | mActionBar.setTitle(title); 129 | } 130 | } 131 | 132 | @Override 133 | public boolean dispatchKeyEvent(KeyEvent event) { 134 | if (event.getKeyCode() == KeyEvent.KEYCODE_BACK 135 | && event.getAction() == KeyEvent.ACTION_DOWN) { 136 | onBackAction(); 137 | return true; 138 | } 139 | return super.dispatchKeyEvent(event); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/activity/ConnectionActivity.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.activity; 2 | 3 | import android.app.Dialog; 4 | import android.app.ProgressDialog; 5 | import android.content.Context; 6 | import android.content.DialogInterface; 7 | import android.content.Intent; 8 | import android.net.wifi.WifiInfo; 9 | import android.net.wifi.WifiManager; 10 | import android.os.Bundle; 11 | import android.os.Handler; 12 | import android.os.Message; 13 | import android.support.v7.app.AlertDialog; 14 | import android.text.TextUtils; 15 | import android.util.Log; 16 | import android.view.View; 17 | import android.widget.AdapterView; 18 | import android.widget.Button; 19 | import android.widget.ListView; 20 | import android.widget.Toast; 21 | 22 | import com.kuoruan.bomberman.R; 23 | import com.kuoruan.bomberman.adapter.ChatAdapter; 24 | import com.kuoruan.bomberman.adapter.ConnectionAdapter; 25 | import com.kuoruan.bomberman.entity.ChatContent; 26 | import com.kuoruan.bomberman.entity.ConnectionItem; 27 | import com.kuoruan.bomberman.net.ConnectingService; 28 | import com.kuoruan.bomberman.util.ConnectConstants; 29 | 30 | import java.util.ArrayList; 31 | import java.util.List; 32 | 33 | /** 34 | * 对战大厅 35 | */ 36 | 37 | public class ConnectionActivity extends BaseActivity implements View.OnClickListener { 38 | private List mConnections = new ArrayList<>(); 39 | private ListView mListView; 40 | private ConnectionAdapter mAdapter; 41 | private String mIP; 42 | private ConnectingService mConnectingService; 43 | // 联机请求对话框 44 | private AlertDialog mConnectDialog; 45 | // 联机请求等待对话框 46 | private ProgressDialog waitDialog; 47 | // 显示聊天对话框 48 | private Dialog mChatDialog; 49 | private ChatAdapter mChatAdapter; 50 | private List mChats = new ArrayList<>(); 51 | private ProgressDialog mScanDialog; 52 | 53 | @Override 54 | protected int getLayoutId() { 55 | return R.layout.activity_connection; 56 | } 57 | 58 | @Override 59 | protected void init(Bundle savedInstanceState) { 60 | mScanDialog = new ProgressDialog(this); 61 | mScanDialog.setMessage(getString(R.string.scan_loading)); 62 | initView(); 63 | initNet(); 64 | } 65 | 66 | /** 67 | * 处理网络回调信息,刷新界面 68 | */ 69 | private Handler mHandler = new Handler() { 70 | 71 | @Override 72 | public void handleMessage(Message msg) { 73 | switch (msg.what) { 74 | case ConnectConstants.ON_JOIN: 75 | ConnectionItem add = getConnectItem(msg); 76 | if (!mConnections.contains(add)) { 77 | mConnections.add(add); 78 | mAdapter.changeData(mConnections); 79 | } 80 | break; 81 | case ConnectConstants.ON_EXIT: 82 | ConnectionItem remove = getConnectItem(msg); 83 | if (mConnections.contains(remove)) { 84 | mConnections.remove(remove); 85 | mAdapter.changeData(mConnections); 86 | } 87 | break; 88 | case ConnectConstants.CONNECT_ASK: 89 | ConnectionItem ask = getConnectItem(msg); 90 | showConnectDialog(ask.name, ask.ip); 91 | break; 92 | case ConnectConstants.CONNECT_AGREE: 93 | if (waitDialog != null && waitDialog.isShowing()) { 94 | waitDialog.dismiss(); 95 | } 96 | String ip = msg.peekData().getString("ip"); 97 | Log.i(TAG, "handleMessage: " + ip); 98 | 99 | WifiGameActivity.startActivity(ConnectionActivity.this, false, ip); 100 | break; 101 | case ConnectConstants.CONNECT_REJECT: 102 | if (waitDialog != null && waitDialog.isShowing()) { 103 | waitDialog.dismiss(); 104 | } 105 | Toast.makeText(ConnectionActivity.this, "对方拒绝了你的请求", Toast.LENGTH_LONG).show(); 106 | default: 107 | break; 108 | } 109 | } 110 | }; 111 | 112 | private void initView() { 113 | Button scan = (Button) findViewById(R.id.scan); 114 | scan.setOnClickListener(this); 115 | mListView = (ListView) findViewById(R.id.list); 116 | mAdapter = new ConnectionAdapter(this, mConnections); 117 | mListView.setAdapter(mAdapter); 118 | mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 119 | 120 | @Override 121 | public void onItemClick(AdapterView parent, View v, int position, long id) { 122 | String ipDst = mConnections.get(position).ip; 123 | mConnectingService.sendAskConnect(ipDst); 124 | String title = "请求对战"; 125 | String message = "等待" + ipDst + "回应.请稍后...."; 126 | showProgressDialog(title, message); 127 | } 128 | }); 129 | } 130 | 131 | private void initNet() { 132 | mIP = getIp(); 133 | if (TextUtils.isEmpty(mIP)) { 134 | Toast.makeText(this, "请检查wifi连接后重试", Toast.LENGTH_LONG).show(); 135 | finish(); 136 | } 137 | 138 | } 139 | 140 | @Override 141 | protected void onStart() { 142 | super.onStart(); 143 | mConnectingService = new ConnectingService(mIP, mHandler); 144 | mConnectingService.start(); 145 | mConnectingService.sendScanMsg(); 146 | } 147 | 148 | @Override 149 | protected void onStop() { 150 | super.onStop(); 151 | mConnectingService.stop(); 152 | mConnectingService.sendExitMsg(); 153 | } 154 | 155 | /** 156 | * 从消息里面获取数据并生成ConnectionItem对象 157 | * 158 | * @param msg 159 | * @return ConnectionItem 160 | */ 161 | private ConnectionItem getConnectItem(Message msg) { 162 | Bundle data = msg.peekData(); 163 | String name = data.getString("name"); 164 | String ip = data.getString("ip"); 165 | ConnectionItem ci = new ConnectionItem(name, ip); 166 | return ci; 167 | } 168 | 169 | /** 170 | * 获取本机的ip地址,通过wifi连接局域网的情况 171 | * 172 | * @return ip地址 173 | */ 174 | private String getIp() { 175 | WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE); 176 | // 检查Wifi状态 177 | if (!wm.isWifiEnabled()) { 178 | Log.d(TAG, "wifi is not enable,enable wifi first"); 179 | return null; 180 | } 181 | WifiInfo wi = wm.getConnectionInfo(); 182 | // 获取32位整型IP地址 183 | int ipAdd = wi.getIpAddress(); 184 | // 把整型地址转换成“*.*.*.*”地址 185 | String ip = intToIp(ipAdd); 186 | Log.d(TAG, "ip:" + ip); 187 | return ip; 188 | } 189 | 190 | private String intToIp(int i) { 191 | return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF) 192 | + "." + (i >> 24 & 0xFF); 193 | } 194 | 195 | @Override 196 | public void onClick(View v) { 197 | switch (v.getId()) { 198 | case R.id.scan: 199 | mScanDialog.show(); 200 | mConnectingService.sendScanMsg(); 201 | break; 202 | 203 | default: 204 | break; 205 | } 206 | } 207 | 208 | 209 | private void showConnectDialog(String name, final String ip) { 210 | String msg = name + getString(R.string.fight_request); 211 | DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { 212 | 213 | @Override 214 | public void onClick(DialogInterface dialog, int which) { 215 | switch (which) { 216 | case DialogInterface.BUTTON_POSITIVE: 217 | mConnectingService.accept(ip); 218 | WifiGameActivity.startActivity(ConnectionActivity.this, true, ip); 219 | break; 220 | case DialogInterface.BUTTON_NEGATIVE: 221 | mConnectingService.reject(ip); 222 | break; 223 | default: 224 | break; 225 | } 226 | } 227 | 228 | }; 229 | if (mConnectDialog == null) { 230 | AlertDialog.Builder b = new AlertDialog.Builder(this); 231 | b.setCancelable(false); 232 | b.setMessage(msg); 233 | b.setPositiveButton(R.string.agree, listener); 234 | b.setNegativeButton(R.string.reject, listener); 235 | mConnectDialog = b.create(); 236 | } else { 237 | mConnectDialog.setMessage(msg); 238 | mConnectDialog.setButton(DialogInterface.BUTTON_POSITIVE, 239 | getText(R.string.agree), listener); 240 | mConnectDialog.setButton(DialogInterface.BUTTON_NEGATIVE, 241 | getText(R.string.reject), listener); 242 | } 243 | if (!mConnectDialog.isShowing()) { 244 | mConnectDialog.show(); 245 | } 246 | } 247 | 248 | /** 249 | * 显示聊天内容对话框 250 | */ 251 | private void showChatDialog() { 252 | if (mChatDialog == null) { 253 | AlertDialog.Builder b = new AlertDialog.Builder(this); 254 | b.setTitle("对话"); 255 | View view = getLayoutInflater().inflate(R.layout.chat_dialog, null); 256 | ListView list = (ListView) view.findViewById(R.id.list_chat); 257 | mChatAdapter = new ChatAdapter(this, mChats); 258 | list.setAdapter(mChatAdapter); 259 | b.setView(view); 260 | mChatDialog = b.create(); 261 | mChatDialog.show(); 262 | } else { 263 | if (mChatDialog.isShowing()) { 264 | mChatAdapter.notifyDataSetChanged(); 265 | } else { 266 | mChatDialog.show(); 267 | } 268 | } 269 | } 270 | 271 | private void showProgressDialog(String title, String message) { 272 | if (waitDialog == null) { 273 | waitDialog = new ProgressDialog(this); 274 | } 275 | //waitDialog.setTitle(title); 276 | waitDialog.setMessage(message); 277 | waitDialog.setIndeterminate(true); 278 | waitDialog.setCancelable(true); 279 | waitDialog.show(); 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/activity/GameActivity.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.activity; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.os.Handler; 6 | import android.os.Message; 7 | import android.util.Log; 8 | import android.view.Display; 9 | import android.view.Window; 10 | import android.widget.Toast; 11 | 12 | import com.kuoruan.bomberman.util.GameConstants; 13 | import com.kuoruan.bomberman.util.PlayerManager; 14 | import com.kuoruan.bomberman.util.SceneManager; 15 | import com.kuoruan.bomberman.view.GameView; 16 | 17 | /** 18 | * Created by Liao on 2016/5/1 0001. 19 | */ 20 | public class GameActivity extends BaseActivity { 21 | 22 | private GameView mGameView = null; 23 | private PlayerManager mPlayerManager; 24 | private SceneManager mSceneManager; 25 | 26 | private Context mContext; 27 | private Handler mRefreshHandler = new Handler() { 28 | @Override 29 | public void handleMessage(Message msg) { 30 | Toast.makeText(mContext, "start...", Toast.LENGTH_LONG); 31 | super.handleMessage(msg); 32 | } 33 | }; 34 | 35 | @Override 36 | protected int getLayoutId() { 37 | return 0; 38 | } 39 | 40 | @Override 41 | protected void init(Bundle savedInstanceState) { 42 | mContext = getApplicationContext(); 43 | 44 | Display display = getWindowManager().getDefaultDisplay(); 45 | 46 | int playerId = 1; 47 | mPlayerManager = new PlayerManager(mContext, mRefreshHandler, playerId); 48 | mSceneManager = new SceneManager(mContext, mRefreshHandler, GameConstants.MULTI_PLAYER_STAGE); 49 | 50 | mGameView = new GameView(mContext, mPlayerManager, mSceneManager, display); 51 | 52 | setContentView(mGameView); 53 | } 54 | 55 | @Override 56 | protected void onPause() { 57 | mGameView.getGameThread().setState(GameView.STATE_PAUSED); 58 | super.onPause(); 59 | } 60 | 61 | @Override 62 | protected boolean hasActionBar() { 63 | return false; 64 | } 65 | 66 | @Override 67 | protected void onBeforeSetContentView() { 68 | requestWindowFeature(Window.FEATURE_NO_TITLE); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.activity; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | import com.kuoruan.bomberman.R; 9 | 10 | public class MainActivity extends BaseActivity implements View.OnClickListener { 11 | private static final String TAG = "MainActivity"; 12 | 13 | private TextView tv_create_game; 14 | private TextView tv_join_game; 15 | private TextView tv_exit_game; 16 | 17 | @Override 18 | protected int getLayoutId() { 19 | return R.layout.activity_main; 20 | } 21 | 22 | @Override 23 | protected void init(Bundle savedInstanceState) { 24 | initView(); 25 | } 26 | 27 | private void initView() { 28 | tv_create_game = (TextView) findViewById(R.id.bt_single_play); 29 | tv_join_game = (TextView) findViewById(R.id.bt_multi_game); 30 | tv_exit_game = (TextView) findViewById(R.id.bt_exit_game); 31 | tv_create_game.setOnClickListener(this); 32 | tv_join_game.setOnClickListener(this); 33 | tv_exit_game.setOnClickListener(this); 34 | } 35 | 36 | @Override 37 | public void onClick(View v) { 38 | switch (v.getId()) { 39 | case R.id.bt_single_play: 40 | startActivity(new Intent(this, GameActivity.class)); 41 | break; 42 | case R.id.bt_multi_game: 43 | startActivity(new Intent(this, ConnectionActivity.class)); 44 | break; 45 | } 46 | } 47 | 48 | @Override 49 | protected boolean hasBackButton() { 50 | return false; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/activity/SplashActivity.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.activity; 2 | 3 | import android.os.Bundle; 4 | 5 | /** 6 | * Created by Liao on 2016/6/16 0016. 7 | */ 8 | 9 | public class SplashActivity extends BaseActivity { 10 | 11 | @Override 12 | protected int getLayoutId() { 13 | return 0; 14 | } 15 | 16 | @Override 17 | protected void init(Bundle savedInstanceState) { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/activity/WifiGameActivity.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.activity; 2 | 3 | import android.app.ProgressDialog; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.os.Handler; 8 | import android.os.Message; 9 | import android.text.TextUtils; 10 | import android.util.Log; 11 | import android.view.Display; 12 | import android.view.Window; 13 | import android.widget.Toast; 14 | 15 | import com.kuoruan.bomberman.net.ConnectedService; 16 | import com.kuoruan.bomberman.util.ConnectConstants; 17 | import com.kuoruan.bomberman.util.GameConstants; 18 | import com.kuoruan.bomberman.util.PlayerManager; 19 | import com.kuoruan.bomberman.util.SceneManager; 20 | import com.kuoruan.bomberman.view.GameView; 21 | 22 | import org.json.JSONObject; 23 | 24 | /** 25 | * Created by Liao on 2016/6/16 0016. 26 | */ 27 | public class WifiGameActivity extends BaseActivity { 28 | 29 | private GameView mGameView; 30 | private PlayerManager mPlayerManager; 31 | private SceneManager mSceneManager; 32 | 33 | private Context mContext; 34 | private boolean mIsServer; 35 | private String mDstIp; 36 | private ProgressDialog mWaitDialog; 37 | private ConnectedService mConnectedService; 38 | 39 | /** 40 | * 处理游戏回调信息,刷新界面 41 | */ 42 | private Handler mRefreshHandler = new Handler() { 43 | 44 | @Override 45 | public void handleMessage(Message msg) { 46 | JSONObject jsonObject = (JSONObject) msg.obj; 47 | switch (msg.what) { 48 | case GameConstants.PLAYER_ADD: 49 | case GameConstants.PLAYER_MOVE: 50 | case GameConstants.PLAYER_STOP: 51 | case GameConstants.PLAYER_DIE: 52 | case GameConstants.SET_BOMB: 53 | mConnectedService.sendTCPData(jsonObject); 54 | break; 55 | default: 56 | break; 57 | } 58 | } 59 | }; 60 | 61 | /** 62 | * 处理网络信息,更新界面 63 | */ 64 | private Handler mRequestHandler = new Handler() { 65 | 66 | @Override 67 | public void handleMessage(Message msg) { 68 | JSONObject jsonObject = (JSONObject) msg.obj; 69 | switch (msg.what) { 70 | case ConnectConstants.GAME_CONNECTED: 71 | mWaitDialog.dismiss(); 72 | break; 73 | case GameConstants.PLAYER_ADD: 74 | mPlayerManager.handlePlayerAdd(jsonObject); 75 | break; 76 | case GameConstants.PLAYER_MOVE: 77 | mPlayerManager.handlePlayerMove(jsonObject); 78 | break; 79 | case GameConstants.PLAYER_STOP: 80 | mPlayerManager.handlePlayerStop(jsonObject); 81 | break; 82 | case GameConstants.PLAYER_DIE: 83 | mPlayerManager.handlePlayerDie(jsonObject); 84 | break; 85 | case GameConstants.SET_BOMB: 86 | mSceneManager.handleSetBomb(jsonObject); 87 | break; 88 | default: 89 | break; 90 | } 91 | } 92 | }; 93 | 94 | 95 | public static void startActivity(Context context, boolean isServer, String dstIp) { 96 | Intent intent = new Intent(context, WifiGameActivity.class); 97 | Bundle b = new Bundle(); 98 | b.putBoolean("isServer", isServer); 99 | b.putString("ip", dstIp); 100 | intent.putExtras(b); 101 | context.startActivity(intent); 102 | } 103 | 104 | @Override 105 | protected int getLayoutId() { 106 | return 0; 107 | } 108 | 109 | @Override 110 | protected void init(Bundle savedInstanceState) { 111 | Bundle b = getIntent().getExtras(); 112 | if (b == null) { 113 | Toast.makeText(this, "建立网络失败,请重试", Toast.LENGTH_SHORT).show(); 114 | finish(); 115 | } 116 | 117 | showProgressDialog(null, "建立连接中,请稍后"); 118 | mContext = getApplicationContext(); 119 | mIsServer = b.getBoolean("isServer"); 120 | mDstIp = b.getString("ip"); 121 | initGame(); 122 | } 123 | 124 | private void initGame() { 125 | Display display = getWindowManager().getDefaultDisplay(); 126 | mConnectedService = new ConnectedService(mRequestHandler, mDstIp, mIsServer); // TCP连接 127 | int playerId = mIsServer ? 1 : 2; 128 | 129 | mPlayerManager = new PlayerManager(mContext, mRefreshHandler, playerId); 130 | mSceneManager = new SceneManager(mContext, mRefreshHandler, GameConstants.MULTI_PLAYER_STAGE); 131 | 132 | mGameView = new GameView(mContext, mPlayerManager, mSceneManager, display); 133 | 134 | setContentView(mGameView); 135 | } 136 | 137 | @Override 138 | protected void onPause() { 139 | mGameView.getGameThread().setState(GameView.STATE_PAUSED); 140 | super.onPause(); 141 | } 142 | 143 | @Override 144 | protected boolean hasActionBar() { 145 | return false; 146 | } 147 | 148 | @Override 149 | protected void onBeforeSetContentView() { 150 | requestWindowFeature(Window.FEATURE_NO_TITLE); 151 | } 152 | 153 | private void showProgressDialog(String title, String message) { 154 | if (mWaitDialog == null) { 155 | mWaitDialog = new ProgressDialog(this); 156 | } 157 | if (!TextUtils.isEmpty(title)) { 158 | mWaitDialog.setTitle(title); 159 | } 160 | mWaitDialog.setMessage(message); 161 | mWaitDialog.setIndeterminate(true); 162 | mWaitDialog.setCancelable(true); 163 | mWaitDialog.show(); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/adapter/ChatAdapter.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.TextView; 9 | 10 | import com.kuoruan.bomberman.R; 11 | import com.kuoruan.bomberman.entity.ChatContent; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public class ChatAdapter extends BaseAdapter { 17 | private List mData = new ArrayList<>(); 18 | private Context mContext; 19 | 20 | public ChatAdapter(Context context, List data) { 21 | mContext = context; 22 | mData = data; 23 | } 24 | 25 | @Override 26 | public int getCount() { 27 | return mData.size(); 28 | } 29 | 30 | @Override 31 | public Object getItem(int position) { 32 | return mData.get(position); 33 | } 34 | 35 | @Override 36 | public long getItemId(int position) { 37 | return 0; 38 | } 39 | 40 | @Override 41 | public View getView(int position, View convertView, ViewGroup parent) { 42 | ViewHolder holder; 43 | if (convertView == null) { 44 | LayoutInflater inflate = (LayoutInflater) mContext 45 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 46 | convertView = inflate.inflate(R.layout.chat_item, null); 47 | holder = new ViewHolder(); 48 | holder.title = (TextView) convertView.findViewById(R.id.title); 49 | holder.content = (TextView) convertView.findViewById(R.id.content); 50 | holder.time = (TextView) convertView.findViewById(R.id.time); 51 | convertView.setTag(holder); 52 | } else { 53 | holder = (ViewHolder) convertView.getTag(); 54 | } 55 | int count = getCount() - 1; 56 | ChatContent item = mData.get(count - position); 57 | holder.title.setText(item.connector.name + "(" + item.connector.ip 58 | + ")"); 59 | holder.content.setText(item.content); 60 | holder.time.setText(item.time); 61 | return convertView; 62 | } 63 | 64 | public void changeData(List data) { 65 | mData = data; 66 | notifyDataSetChanged(); 67 | } 68 | 69 | class ViewHolder { 70 | TextView title; 71 | TextView content; 72 | TextView time; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/adapter/ConnectionAdapter.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.TextView; 9 | 10 | import com.kuoruan.bomberman.R; 11 | import com.kuoruan.bomberman.entity.ConnectionItem; 12 | 13 | import java.util.List; 14 | 15 | public class ConnectionAdapter extends BaseAdapter { 16 | private List mData; 17 | private Context mContext; 18 | 19 | public ConnectionAdapter(Context context, List data) { 20 | this.mContext = context; 21 | this.mData = data; 22 | } 23 | 24 | @Override 25 | public int getCount() { 26 | if (mData != null) { 27 | return mData.size(); 28 | } 29 | return 0; 30 | } 31 | 32 | @Override 33 | public ConnectionItem getItem(int position) { 34 | if (mData != null) { 35 | return mData.get(position); 36 | } 37 | return null; 38 | } 39 | 40 | @Override 41 | public long getItemId(int position) { 42 | return 0; 43 | } 44 | 45 | @Override 46 | public View getView(int position, View convertView, ViewGroup parent) { 47 | ViewHolder holder; 48 | if (convertView == null) { 49 | LayoutInflater inflate = (LayoutInflater) mContext 50 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 51 | convertView = inflate.inflate(R.layout.connection_item, null); 52 | holder = new ViewHolder(); 53 | holder.name = (TextView) convertView.findViewById(R.id.name); 54 | holder.ip = (TextView) convertView.findViewById(R.id.ip); 55 | convertView.setTag(holder); 56 | } else { 57 | holder = (ViewHolder) convertView.getTag(); 58 | } 59 | ConnectionItem item = mData.get(position); 60 | holder.name.setText(mContext.getString(R.string.game_player) + item.name); 61 | holder.ip.setText("IP地址" + item.ip); 62 | return convertView; 63 | } 64 | 65 | public void changeData(List data) { 66 | mData = data; 67 | notifyDataSetChanged(); 68 | } 69 | 70 | class ViewHolder { 71 | TextView name; 72 | TextView ip; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/dao/GameDataDao.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.dao; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.database.sqlite.SQLiteDatabase; 6 | 7 | import com.kuoruan.bomberman.entity.GameTile; 8 | import com.kuoruan.bomberman.entity.data.GameData; 9 | 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | import static android.provider.BaseColumns._ID; 16 | 17 | /** 18 | * Created by Liao on 2016/5/1 0001. 19 | */ 20 | public class GameDataDao { 21 | 22 | public static final String TABLE_NAME = "gameUnitData"; 23 | 24 | public static final String TYPE = "type"; 25 | public static final String SUB_TYPE = "subType"; 26 | public static final String DRAWABLE = "drawable"; 27 | public static final String VISIBLE = "visible"; 28 | 29 | //图片类型 30 | public static final int TYPE_TILE = 1; 31 | public static final int TYPE_PLAYER = 2; 32 | public static final int TYPE_BOMB = 3; 33 | public static final int TYPE_BOMB_FIRE = 4; 34 | 35 | public static final int FIELD_ID_ID = 0; 36 | public static final int FIELD_ID_TYPE = 1; 37 | public static final int FIELD_ID_SUB_TYPE = 2; 38 | public static final int FIELD_ID_DRAWABLE = 3; 39 | public static final int FIELD_ID_VISIBLE = 4; 40 | 41 | private GameOpenHelper mGameOpenHelper; 42 | private Context mContext; 43 | private static GameDataDao mGameData; 44 | 45 | private GameDataDao(Context context) { 46 | this.mContext = context; 47 | mGameOpenHelper = new GameOpenHelper(context); 48 | } 49 | 50 | public static GameDataDao getInstance(Context context) { 51 | if (mGameData == null) { 52 | synchronized (GameDataDao.class) { 53 | if (mGameData == null) { 54 | mGameData = new GameDataDao(context); 55 | } 56 | } 57 | } 58 | return mGameData; 59 | } 60 | 61 | public Map getGameTileData() { 62 | SQLiteDatabase db = mGameOpenHelper.getReadableDatabase(); 63 | 64 | String[] from = {_ID, TYPE, SUB_TYPE, DRAWABLE, VISIBLE}; 65 | String where = TYPE + " = " + TYPE_TILE; 66 | 67 | Cursor cursor = db.query(TABLE_NAME, from, where, null, null, null, null); 68 | 69 | Map data = new HashMap<>(); 70 | 71 | while (cursor.moveToNext()) { 72 | GameData tileData = new GameData(); 73 | tileData.setId(cursor.getInt(FIELD_ID_ID)); 74 | tileData.setType(cursor.getInt(FIELD_ID_TYPE)); 75 | int subType = cursor.getInt(FIELD_ID_SUB_TYPE); 76 | tileData.setSubType(subType); 77 | tileData.setDrawable(cursor.getInt(FIELD_ID_DRAWABLE)); 78 | int visible = cursor.getInt(FIELD_ID_VISIBLE); 79 | tileData.setVisible(visible == 1); 80 | data.put(subType, tileData); 81 | } 82 | cursor.close(); 83 | db.close(); 84 | 85 | return data; 86 | } 87 | 88 | public Map getPlayerData() { 89 | SQLiteDatabase db = mGameOpenHelper.getReadableDatabase(); 90 | 91 | String[] from = {_ID, TYPE, SUB_TYPE, DRAWABLE}; 92 | String where = TYPE + " = " + TYPE_PLAYER; 93 | 94 | Cursor cursor = db.query(TABLE_NAME, from, where, null, null, null, null); 95 | Map data = new HashMap<>(); 96 | 97 | while (cursor.moveToNext()) { 98 | GameData playerData = new GameData(); 99 | 100 | playerData.setId(cursor.getInt(FIELD_ID_ID)); 101 | playerData.setType(cursor.getInt(FIELD_ID_TYPE)); 102 | int id = cursor.getInt(FIELD_ID_SUB_TYPE); 103 | playerData.setSubType(id); 104 | playerData.setDrawable(cursor.getInt(FIELD_ID_DRAWABLE)); 105 | 106 | data.put(id, playerData); 107 | } 108 | 109 | cursor.close(); 110 | db.close(); 111 | 112 | return data; 113 | } 114 | 115 | public Map getBombData() { 116 | SQLiteDatabase db = mGameOpenHelper.getReadableDatabase(); 117 | String[] from = {_ID, TYPE, SUB_TYPE, DRAWABLE}; 118 | String where = TYPE + " = " + TYPE_BOMB; 119 | 120 | Cursor cursor = db.query(TABLE_NAME, from, where, null, null, null, null); 121 | 122 | Map data = new HashMap<>(); 123 | 124 | while (cursor.moveToNext()) { 125 | GameData bombData = new GameData(); 126 | bombData.setId(cursor.getInt(FIELD_ID_ID)); 127 | bombData.setType(cursor.getInt(FIELD_ID_TYPE)); 128 | int subType = cursor.getInt(FIELD_ID_SUB_TYPE); 129 | bombData.setSubType(subType); 130 | bombData.setDrawable(cursor.getInt(FIELD_ID_DRAWABLE)); 131 | 132 | data.put(subType, bombData); 133 | } 134 | 135 | cursor.close(); 136 | db.close(); 137 | 138 | return data; 139 | } 140 | 141 | public Map getFireData() { 142 | SQLiteDatabase db = mGameOpenHelper.getReadableDatabase(); 143 | String[] from = {_ID, TYPE, SUB_TYPE, DRAWABLE}; 144 | String where = TYPE + " = " + TYPE_BOMB_FIRE; 145 | Cursor cursor = db.query(TABLE_NAME, from, where, null, null, null, null); 146 | 147 | Map data = new HashMap<>(); 148 | 149 | while (cursor.moveToNext()) { 150 | GameData fireData = new GameData(); 151 | fireData.setId(cursor.getInt(FIELD_ID_ID)); 152 | fireData.setType(cursor.getInt(FIELD_ID_TYPE)); 153 | int subType = cursor.getInt(FIELD_ID_SUB_TYPE); 154 | fireData.setSubType(subType); 155 | fireData.setDrawable(cursor.getInt(FIELD_ID_DRAWABLE)); 156 | 157 | data.put(subType, fireData); 158 | } 159 | cursor.close(); 160 | db.close(); 161 | 162 | return data; 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/dao/GameLevelDataDao.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.dao; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.database.sqlite.SQLiteDatabase; 6 | 7 | import com.kuoruan.bomberman.entity.data.GameLevelData; 8 | 9 | import static android.provider.BaseColumns._ID; 10 | 11 | /** 12 | * Created by Liao on 2016/5/2 0002. 13 | */ 14 | public class GameLevelDataDao { 15 | public static final String TABLE_NAME = "gameLevelTileData"; 16 | 17 | public static final String STAGE = "stage"; 18 | public static final String PLAYER_START_TILE_X = "playerStartX"; 19 | public static final String PLAYER_START_TILE_Y = "playerStartY"; 20 | public static final String TILE_DATA = "tileData"; 21 | 22 | public static final int FIELD_ID_ID = 0; 23 | public static final int FIELD_ID_STAGE = 1; 24 | public static final int FIELD_ID_PLAYER_START_X = 2; 25 | public static final int FIELD_ID_PLAYER_START_Y = 3; 26 | public static final int FIELD_ID_TILE_DATA = 4; 27 | 28 | public static final String TILE_DATA_LINE_BREAK = "//"; 29 | 30 | private static GameLevelDataDao mGameLevelTileDao; 31 | private GameOpenHelper mGameOpenHelper; 32 | private Context mContext; 33 | 34 | private GameLevelDataDao(Context context) { 35 | mGameOpenHelper = new GameOpenHelper(context); 36 | this.mContext = context; 37 | } 38 | 39 | public static GameLevelDataDao getInstance(Context context) { 40 | if (mGameLevelTileDao == null) { 41 | synchronized (GameLevelDataDao.class) { 42 | if (mGameLevelTileDao == null) { 43 | mGameLevelTileDao = new GameLevelDataDao(context); 44 | } 45 | } 46 | } 47 | 48 | return mGameLevelTileDao; 49 | } 50 | 51 | /** 52 | * 获取关卡地图数据集合 53 | * 54 | * @param stage 关卡 55 | * @return ArrayList 56 | */ 57 | public GameLevelData getGameLevelData(int stage) { 58 | 59 | SQLiteDatabase db = mGameOpenHelper.getReadableDatabase(); 60 | 61 | String[] from = {_ID, STAGE, PLAYER_START_TILE_X, PLAYER_START_TILE_Y, TILE_DATA}; 62 | String where = STAGE + " = " + stage; 63 | 64 | Cursor cursor = db.query(TABLE_NAME, from, where, null, null, null, null); 65 | 66 | GameLevelData gameLevelData = null; 67 | if (cursor.moveToNext()) { 68 | gameLevelData = new GameLevelData(); 69 | gameLevelData.setId(cursor.getInt(FIELD_ID_ID)); 70 | gameLevelData.setPlayerStartX(cursor.getInt(FIELD_ID_PLAYER_START_X)); 71 | gameLevelData.setPlayerStartY(cursor.getInt(FIELD_ID_PLAYER_START_Y)); 72 | gameLevelData.setStage(cursor.getInt(FIELD_ID_STAGE)); 73 | gameLevelData.setLevelTiles(cursor.getString(FIELD_ID_TILE_DATA)); 74 | } 75 | cursor.close(); 76 | db.close(); 77 | return gameLevelData; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/dao/GameOpenHelper.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.dao; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.database.sqlite.SQLiteOpenHelper; 6 | 7 | import com.kuoruan.bomberman.R; 8 | import com.kuoruan.bomberman.entity.Bomb; 9 | import com.kuoruan.bomberman.entity.BombFire; 10 | import com.kuoruan.bomberman.entity.GameTile; 11 | import com.kuoruan.bomberman.util.GameConstants; 12 | 13 | import static android.provider.BaseColumns._ID; 14 | 15 | /** 16 | * Created by Liao on 2016/5/2 0002. 17 | */ 18 | public class GameOpenHelper extends SQLiteOpenHelper { 19 | 20 | private static final String DB_NAME = "bomberman.db"; 21 | private static final int DB_VERSION = 1; 22 | 23 | public GameOpenHelper(Context context) { 24 | super(context, DB_NAME, null, DB_VERSION); 25 | } 26 | 27 | @Override 28 | public void onCreate(SQLiteDatabase db) { 29 | db.execSQL(CREATE_TABLE_GAME_DATA); 30 | db.execSQL(CREATE_TABLE_GAME_LEVEL_TILES); 31 | 32 | for (String query : POPULATE_TABLE_GAME_DATA) { 33 | db.execSQL(query); 34 | } 35 | 36 | for (String query : POPULATE_TABLE_GAME_LEVEL_TILES) { 37 | db.execSQL(query); 38 | } 39 | } 40 | 41 | @Override 42 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 43 | db.execSQL("DROP TABLE IF EXISTS " + GameDataDao.TABLE_NAME); 44 | db.execSQL("DROP TABLE IF EXISTS " + GameLevelDataDao.TABLE_NAME); 45 | 46 | onCreate(db); 47 | } 48 | 49 | private static final String CREATE_TABLE_GAME_DATA = "CREATE TABLE " + GameDataDao.TABLE_NAME + " (" 50 | + _ID + " INTEGER PRIMARY KEY, " 51 | + GameDataDao.TYPE + " INTEGER DEFAULT 1," 52 | + GameDataDao.SUB_TYPE + " INTEGER DEFAULT 0," 53 | + GameDataDao.DRAWABLE + " INTEGER DEFAULT 0," 54 | + GameDataDao.VISIBLE + " INTEGER DEFAULT 1" 55 | + ");"; 56 | 57 | private static final String CREATE_TABLE_GAME_LEVEL_TILES = "CREATE TABLE " + GameLevelDataDao.TABLE_NAME + " (" 58 | + _ID + " INTEGER PRIMARY KEY AUTOINCREMENT," 59 | + GameLevelDataDao.STAGE + " INTEGER DEFAULT 0," 60 | + GameLevelDataDao.PLAYER_START_TILE_X + " INTEGER DEFAULT 0," 61 | + GameLevelDataDao.PLAYER_START_TILE_Y + " INTEGER DEFAULT 0," 62 | + GameLevelDataDao.TILE_DATA + " TEXT NOT NULL" 63 | + ");"; 64 | 65 | private static final String[] POPULATE_TABLE_GAME_DATA = { 66 | "INSERT INTO " + GameDataDao.TABLE_NAME + " VALUES " 67 | + "(1," + GameDataDao.TYPE_TILE + "," + GameTile.TYPE_OBSTACLE + "," + R.drawable.tile_01 + ",1);", 68 | 69 | "INSERT INTO " + GameDataDao.TABLE_NAME + " VALUES " 70 | + "(2," + GameDataDao.TYPE_TILE + "," + GameTile.TYPE_ROCK + "," + R.drawable.tile_02 + ",1);", 71 | 72 | "INSERT INTO " + GameDataDao.TABLE_NAME + " VALUES " 73 | + "(3," + GameDataDao.TYPE_TILE + "," + GameTile.TYPE_CRATES + "," + R.drawable.tile_03 + ",1);", 74 | 75 | "INSERT INTO " + GameDataDao.TABLE_NAME + " VALUES " 76 | + "(4," + GameDataDao.TYPE_PLAYER + ",1," + R.drawable.player_1 + ",1);", 77 | 78 | "INSERT INTO " + GameDataDao.TABLE_NAME + " VALUES " 79 | + "(5," + GameDataDao.TYPE_PLAYER + ",2," + R.drawable.player_1 + ",1);", 80 | 81 | "INSERT INTO " + GameDataDao.TABLE_NAME + " VALUES " 82 | + "(6," + GameDataDao.TYPE_BOMB + "," + Bomb.NORMAL + "," + R.drawable.bomb_1 + ",1);", 83 | 84 | "INSERT INTO " + GameDataDao.TABLE_NAME + " VALUES " 85 | + "(7," + GameDataDao.TYPE_BOMB_FIRE + "," + BombFire.NORMAL + "," + R.drawable.fire + ",1);", 86 | }; 87 | 88 | private static final String[] POPULATE_TABLE_GAME_LEVEL_TILES = { 89 | "INSERT INTO " + GameLevelDataDao.TABLE_NAME + " VALUES " 90 | + "(null," + GameConstants.MULTI_PLAYER_STAGE + ",1,1,\"" 91 | // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 92 | /* 1 */ + "01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01" + GameLevelDataDao.TILE_DATA_LINE_BREAK 93 | /* 2 */ + "01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,01" + GameLevelDataDao.TILE_DATA_LINE_BREAK 94 | /* 3 */ + "01,00,02,00,02,00,02,00,02,00,02,00,02,00,02,00,01" + GameLevelDataDao.TILE_DATA_LINE_BREAK 95 | /* 4 */ + "01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,01" + GameLevelDataDao.TILE_DATA_LINE_BREAK 96 | /* 5 */ + "01,00,02,00,02,00,02,00,02,00,02,00,02,00,02,00,01" + GameLevelDataDao.TILE_DATA_LINE_BREAK 97 | /* 6 */ + "01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,01" + GameLevelDataDao.TILE_DATA_LINE_BREAK 98 | /* 7 */ + "01,00,02,00,02,00,02,00,02,00,02,00,02,00,02,00,01" + GameLevelDataDao.TILE_DATA_LINE_BREAK 99 | /* 8 */ + "01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,01" + GameLevelDataDao.TILE_DATA_LINE_BREAK 100 | /* 9 */ + "01,00,02,00,02,00,02,00,02,00,02,00,02,00,02,00,01" + GameLevelDataDao.TILE_DATA_LINE_BREAK 101 | /* 10 */ + "01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,01" + GameLevelDataDao.TILE_DATA_LINE_BREAK 102 | /* 11 */ + "01,00,02,00,02,00,02,00,02,00,02,00,02,00,02,00,01" + GameLevelDataDao.TILE_DATA_LINE_BREAK 103 | /* 12 */ + "01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,01" + GameLevelDataDao.TILE_DATA_LINE_BREAK 104 | /* 13 */ + "01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01,01" + GameLevelDataDao.TILE_DATA_LINE_BREAK 105 | + "\");" 106 | }; 107 | } 108 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/entity/BaseImage.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.entity; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Point; 5 | 6 | /** 7 | * 基本图片类,为所有游戏对象基类 8 | */ 9 | 10 | public class BaseImage { 11 | protected Bitmap mBitmap; 12 | protected int mWidth = 0; 13 | protected int mHeight = 0; 14 | protected Point mPoint = new Point(); 15 | 16 | public BaseImage() { 17 | 18 | } 19 | 20 | public BaseImage(Bitmap bitmap) { 21 | this.mBitmap = bitmap; 22 | this.mWidth = bitmap.getWidth(); 23 | this.mHeight = bitmap.getHeight(); 24 | } 25 | 26 | public BaseImage(Bitmap bitmap, Point point) { 27 | this(bitmap); 28 | this.mPoint = point; 29 | } 30 | 31 | public Bitmap getBitmap() { 32 | return mBitmap; 33 | } 34 | 35 | public void setBitmap(Bitmap bitmap) { 36 | if (bitmap != null) { 37 | this.mBitmap = bitmap; 38 | this.mWidth = bitmap.getWidth(); 39 | this.mHeight = bitmap.getHeight(); 40 | } 41 | } 42 | 43 | public void setBitmap(Bitmap bitmap, int width, int height) { 44 | if (bitmap != null) { 45 | this.mBitmap = bitmap; 46 | setWidthAndHeight(width, height); 47 | } 48 | } 49 | 50 | //设置需要的图片宽和高 51 | private void setWidthAndHeight(int width, int height) { 52 | if (mBitmap != null) { 53 | this.mWidth = (width > 0) ? width : mBitmap.getWidth(); 54 | this.mHeight = (height > 0) ? height : ((width > 0) ? width : mBitmap.getHeight()); 55 | } 56 | } 57 | 58 | public int getWidth() { 59 | return mWidth; 60 | } 61 | 62 | public void setWidth(int width) { 63 | mWidth = width; 64 | } 65 | 66 | public int getHeight() { 67 | return mHeight; 68 | } 69 | 70 | public void setHeight(int height) { 71 | mHeight = height; 72 | } 73 | 74 | public int getX() { 75 | return mPoint.x; 76 | } 77 | 78 | public void setX(int x) { 79 | mPoint.x = x; 80 | } 81 | 82 | public int getY() { 83 | return mPoint.y; 84 | } 85 | 86 | public void setY(int y) { 87 | mPoint.y = y; 88 | } 89 | 90 | public void setCenterX(int centerX) { 91 | mPoint.x = (centerX - (this.getWidth() / 2)); 92 | } 93 | 94 | public int getCenterX() { 95 | return (mPoint.x + (this.getWidth() / 2)); 96 | } 97 | 98 | public void setCenterY(int centerY) { 99 | mPoint.y = (centerY - (this.getHeight() / 2)); 100 | } 101 | 102 | public int getCenterY() { 103 | return (mPoint.y + (this.getHeight() / 2)); 104 | } 105 | 106 | public void setPoint(Point point) { 107 | mPoint.x = point.x; 108 | mPoint.y = point.y; 109 | } 110 | 111 | public Point getPoint() { 112 | return mPoint; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/entity/Bomb.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.entity; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Point; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by Liao on 2016/5/3 0003. 10 | */ 11 | public class Bomb extends GameTile { 12 | 13 | /** 14 | * 炸弹类型 15 | */ 16 | public static final int NORMAL = 1; 17 | /** 18 | * 默认爆炸时间,毫秒 19 | */ 20 | public static final int DEFAULT_EXPLOSION_TIME = 3000; 21 | /** 22 | * 默认火焰长度 23 | */ 24 | public static final int DEFAULT_FIRE_LENGTH = 3; 25 | 26 | private long id; 27 | private int pid; 28 | private int mExplosionTime = DEFAULT_EXPLOSION_TIME; 29 | private boolean mExplosion = false; 30 | private int mFireLength = DEFAULT_FIRE_LENGTH; 31 | 32 | public Bomb(List frameBitmap, boolean isLoop, Point point, int pid) { 33 | super(frameBitmap, isLoop, point, GameTile.TYPE_BOMB); 34 | this.id = System.currentTimeMillis(); 35 | this.pid = pid; 36 | new BombThread().start(); 37 | } 38 | 39 | public Bomb(List frameBitmap, boolean isLoop, Point point, long id, int pid) { 40 | this(frameBitmap, isLoop, point, pid); 41 | this.id = id; 42 | } 43 | 44 | public long getId() { 45 | return id; 46 | } 47 | 48 | class BombThread extends Thread { 49 | @Override 50 | public void run() { 51 | try { 52 | Thread.sleep(mExplosionTime); 53 | } catch (InterruptedException e) { 54 | e.printStackTrace(); 55 | } 56 | 57 | if (!mExplosion) { 58 | mExplosion = true; 59 | } 60 | } 61 | } 62 | 63 | public int getExplosionTime() { 64 | return mExplosionTime; 65 | } 66 | 67 | public void setExplosionTime(int explosionTime) { 68 | this.mExplosionTime = explosionTime; 69 | } 70 | 71 | public void setExplosion(boolean explosion) { 72 | mExplosion = explosion; 73 | } 74 | 75 | public boolean isExplosion() { 76 | return mExplosion; 77 | } 78 | 79 | public int getFireLength() { 80 | return mFireLength; 81 | } 82 | 83 | public void setFireLength(int fireLength) { 84 | mFireLength = fireLength; 85 | } 86 | 87 | public int getPid() { 88 | return pid; 89 | } 90 | 91 | public void setPid(int pid) { 92 | this.pid = pid; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/entity/BombFire.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.entity; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Point; 5 | 6 | import com.kuoruan.bomberman.util.SceneManager; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Created by Liao on 2016/5/8 0008. 12 | */ 13 | public class BombFire extends GameTile { 14 | 15 | public static final int NORMAL = 1; 16 | 17 | public static final int TYPE_UP = 1; 18 | public static final int TYPE_DOWN = 2; 19 | public static final int TYPE_LEFT = 3; 20 | public static final int TYPE_RIGHT = 4; 21 | public static final int TYPE_VERTICAL = 5; 22 | public static final int TYPE_HORIZONTAL = 6; 23 | public static final int TYPE_CENTER = 7; 24 | 25 | public BombFire(List frameBitmap) { 26 | super(frameBitmap, GameTile.TYPE_BOMB_FIRE); 27 | this.mWidth = SceneManager.tileWidth; 28 | this.mHeight = SceneManager.tileHeight; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/entity/ChatContent.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.entity; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | import java.util.Locale; 6 | 7 | public class ChatContent { 8 | public ConnectionItem connector; 9 | public String content; 10 | public String time; 11 | public static final String FORMAT = "yyyy-M-d HH:mm:ss"; 12 | 13 | public ChatContent(ConnectionItem ci, String content) { 14 | this.connector = ci; 15 | this.content = content; 16 | this.time = new SimpleDateFormat(FORMAT, Locale.getDefault()).format(new Date()); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/entity/Collision.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.entity; 2 | 3 | /** 4 | * 角色和地图冲突对象 5 | */ 6 | public class Collision { 7 | private static final String TAG = "Conflict"; 8 | public static final int SOLVE_LENGTH = 15; 9 | private GameTile mCollisionTile; 10 | private boolean mSolvable; 11 | private int mNewX; 12 | private int mNewY; 13 | 14 | private int mDirection; 15 | 16 | public GameTile getCollisionTile() { 17 | return mCollisionTile; 18 | } 19 | 20 | public void setCollisionTile(GameTile collisionTile) { 21 | mCollisionTile = collisionTile; 22 | } 23 | 24 | public boolean isSolvable() { 25 | return mSolvable; 26 | } 27 | 28 | public void setSolvable(boolean solvable) { 29 | mSolvable = solvable; 30 | } 31 | 32 | public int getNewX() { 33 | return mNewX; 34 | } 35 | 36 | public void setNewX(int newX) { 37 | mNewX = newX; 38 | } 39 | 40 | public int getNewY() { 41 | return mNewY; 42 | } 43 | 44 | public void setNewY(int newY) { 45 | mNewY = newY; 46 | } 47 | 48 | public int getDirection() { 49 | return mDirection; 50 | } 51 | 52 | public void setDirection(int direction) { 53 | mDirection = direction; 54 | } 55 | 56 | public void solveCollision() { 57 | int tileX = mCollisionTile.getX(); 58 | int tileY = mCollisionTile.getY(); 59 | int width = mCollisionTile.getWidth(); 60 | int height = mCollisionTile.getHeight(); 61 | 62 | switch (mDirection) { 63 | case Player.DIRECTION_LEFT: 64 | case Player.DIRECTION_RIGHT: 65 | if (mNewY > tileY) { 66 | if (mNewY + SOLVE_LENGTH > tileY + height) { 67 | this.mSolvable = true; 68 | this.mNewY = tileY + height; 69 | } else { 70 | this.mSolvable = false; 71 | } 72 | } else { 73 | if (mNewY + height - SOLVE_LENGTH < tileY) { 74 | this.mSolvable = true; 75 | this.mNewY = tileY - height; 76 | } else { 77 | this.mSolvable = false; 78 | } 79 | } 80 | break; 81 | case Player.DIRECTION_UP: 82 | case Player.DIRECTION_DOWN: 83 | if (mNewX > tileX) { 84 | if (mNewX + SOLVE_LENGTH > tileX + width) { 85 | this.mSolvable = true; 86 | this.mNewX = tileX + width; 87 | } else { 88 | this.mSolvable = false; 89 | } 90 | } else { 91 | if (mNewX + width - SOLVE_LENGTH < tileX) { 92 | this.mSolvable = true; 93 | this.mNewX = tileX - width; 94 | } else { 95 | this.mSolvable = false; 96 | } 97 | } 98 | break; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/entity/ConnectionItem.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.entity; 2 | 3 | public class ConnectionItem { 4 | public String name; 5 | public String ip; 6 | 7 | public ConnectionItem(String name, String ip) { 8 | this.name = name; 9 | this.ip = ip; 10 | } 11 | 12 | @Override 13 | public int hashCode() { 14 | return 0; 15 | } 16 | 17 | @Override 18 | public boolean equals(Object o) { 19 | ConnectionItem other = (ConnectionItem) o; 20 | return this.ip.equals(other.ip); 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/entity/DynamicImage.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.entity; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Point; 5 | import android.util.Log; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * 动态图片类,为所有动态游戏对象基类 11 | */ 12 | public class DynamicImage extends BaseImage { 13 | 14 | //是否循环播放 15 | public static boolean LOOP_PLAY = false; 16 | //动画播放间隙时间 17 | private static final int ANIM_TIME = 100; 18 | 19 | //上一帧播放时间 20 | private long mLastPlayTime = 0; 21 | //播放当前帧的ID 22 | private int mPlayID = 0; 23 | //动画frame数量 24 | private int mFrameCount = 0; 25 | //动画资源图片 26 | protected List mFrameBitmap = null; 27 | //是否循环播放 28 | private boolean mIsLoop = LOOP_PLAY; 29 | //是否结束播放结束 30 | private boolean mIsEnd = false; 31 | 32 | public DynamicImage() { 33 | } 34 | 35 | public DynamicImage(List frameBitmap, Point point) { 36 | super(frameBitmap.get(0), point); 37 | this.mFrameBitmap = frameBitmap; 38 | this.mFrameCount = frameBitmap.size(); 39 | } 40 | 41 | public DynamicImage(List frameBitmap, boolean isLoop, Point point) { 42 | this(frameBitmap, point); 43 | this.mIsLoop = isLoop; 44 | } 45 | 46 | public DynamicImage(Bitmap baseImage, List frameBitmap, Point point) { 47 | super(baseImage, point); 48 | setFrameBitmap(frameBitmap); 49 | } 50 | 51 | public DynamicImage(Bitmap baseImage, List frameBitmap, boolean isLoop, Point point) { 52 | this(baseImage, frameBitmap, point); 53 | this.mIsLoop = isLoop; 54 | } 55 | 56 | public List getFrameBitmap() { 57 | return mFrameBitmap; 58 | } 59 | 60 | public void setFrameBitmap(List frameBitmap) { 61 | if (frameBitmap != null) { 62 | mFrameBitmap = frameBitmap; 63 | mFrameCount = frameBitmap.size(); 64 | } 65 | } 66 | 67 | public void doAnimation() { 68 | if (mFrameBitmap == null) { 69 | return; 70 | } 71 | 72 | //如果没有播放结束则继续播放 73 | if (!mIsEnd) { 74 | //设置当前游戏图片 75 | mBitmap = mFrameBitmap.get(mPlayID); 76 | long time = System.currentTimeMillis(); 77 | if (time - mLastPlayTime > ANIM_TIME) { 78 | mPlayID++; 79 | mLastPlayTime = time; 80 | if (mPlayID >= mFrameCount) { 81 | //标志动画播放结束 82 | mIsEnd = true; 83 | if (mIsLoop) { 84 | //设置循环播放 85 | mIsEnd = false; 86 | mPlayID = 0; 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | public boolean isEnd() { 94 | return mIsEnd; 95 | } 96 | 97 | public void setLoop(boolean isLoop) { 98 | mIsLoop = isLoop; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/entity/GameTile.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.entity; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Point; 5 | import android.graphics.Rect; 6 | 7 | import com.kuoruan.bomberman.util.SceneManager; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 地图单元对象 13 | */ 14 | public class GameTile extends DynamicImage { 15 | //空白 16 | public static final int TYPE_EMPTY = 0; 17 | //外岩 18 | public static final int TYPE_OBSTACLE = 1; 19 | //岩石 20 | public static final int TYPE_ROCK = 2; 21 | //箱子 22 | public static final int TYPE_CRATES = 3; 23 | //炸弹 24 | public static final int TYPE_BOMB = 4; 25 | //炸弹火焰 26 | public static final int TYPE_BOMB_FIRE = 5; 27 | //道具 28 | public static final int TYPE_GIFT = 6; 29 | 30 | private int mType = TYPE_EMPTY; 31 | private boolean mVisible = true; 32 | private Rect mCollisionRect = null; 33 | private boolean mIsDynamic = false; 34 | 35 | public GameTile() { 36 | } 37 | 38 | public GameTile(List frameBitmap, int type) { 39 | this.mType = type; 40 | setFrameBitmap(frameBitmap); 41 | } 42 | 43 | public GameTile(Bitmap bitmap, Point point, int type) { 44 | this.mType = type; 45 | setBitmap(bitmap); 46 | setPoint(point); 47 | } 48 | 49 | public GameTile(List frameBitmap, Point point, int type) { 50 | super(frameBitmap, point); 51 | this.mIsDynamic = true; 52 | this.mType = type; 53 | } 54 | 55 | public GameTile(Bitmap baseBitmap, List frameBitmap, Point point, int type) { 56 | super(baseBitmap, frameBitmap, point); 57 | this.mIsDynamic = true; 58 | this.mType = type; 59 | } 60 | 61 | public GameTile(List frameBitmap, boolean isLoop, Point point, int type) { 62 | super(frameBitmap, isLoop, point); 63 | this.mIsDynamic = true; 64 | this.mType = type; 65 | } 66 | 67 | public GameTile(Bitmap baseBitmap, List frameBitmap, boolean isLoop, Point point, int type) { 68 | super(baseBitmap, frameBitmap, isLoop, point); 69 | this.mIsDynamic = true; 70 | this.mType = type; 71 | } 72 | 73 | public boolean isCollision(float x, float y, int width, int height) { 74 | if (this.mCollisionRect == null) { 75 | this.mCollisionRect = new Rect((int) x, (int) y, ((int) x + width), ((int) y + height)); 76 | } else { 77 | this.mCollisionRect.set((int) x, (int) y, ((int) x + width), ((int) y + height)); 78 | } 79 | 80 | return (this.mCollisionRect.intersects(mPoint.x, mPoint.y, (mPoint.x + getWidth()), (mPoint.y + getHeight()))); 81 | } 82 | 83 | public int getType() { 84 | return mType; 85 | } 86 | 87 | public void setType(int type) { 88 | this.mType = type; 89 | } 90 | 91 | public boolean isVisible() { 92 | return this.mVisible; 93 | } 94 | 95 | public void setVisible(boolean visible) { 96 | this.mVisible = visible; 97 | } 98 | 99 | public boolean isDynamic() { 100 | return mIsDynamic; 101 | } 102 | 103 | public void setDynamic(boolean dynamic) { 104 | mIsDynamic = dynamic; 105 | } 106 | 107 | /** 108 | * 是否是物理冲撞块 109 | * 110 | * @return 111 | */ 112 | public boolean isCollisionTile() { 113 | return ((this.mType != GameTile.TYPE_EMPTY) && this.mVisible); 114 | } 115 | 116 | public boolean isBlockerTile() { 117 | return (this.mType != GameTile.TYPE_EMPTY); 118 | } 119 | 120 | public void setMapPoint(int x, int y) { 121 | mPoint.x = x * SceneManager.tileWidth; 122 | mPoint.y = y * SceneManager.tileHeight; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/entity/GameUi.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.entity; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Point; 5 | 6 | /** 7 | * 游戏控制按钮 8 | */ 9 | public class GameUi extends BaseImage { 10 | 11 | //按钮状态 12 | public static final int STATE_NORMAL = 1; 13 | public static final int STATE_INACTIVE = 2; 14 | public static final int STATE_ACTIVE = 3; 15 | public static final int STATE_READY = 4; 16 | 17 | private int mState = STATE_NORMAL; 18 | 19 | public GameUi(Bitmap bitmap){ 20 | super(bitmap); 21 | } 22 | 23 | public int getState() { 24 | return mState; 25 | } 26 | 27 | public void setState(int state) { 28 | this.mState = state; 29 | } 30 | 31 | public void setStateReady() { 32 | this.mState = STATE_READY; 33 | } 34 | 35 | public boolean isStateNormal() { 36 | return (this.mState == STATE_NORMAL); 37 | } 38 | 39 | //判断点是否在当前对象中 40 | public boolean getImpact(int x, int y) { 41 | if ((x >= mPoint.x) && (x <= (mPoint.x + mWidth))) { 42 | if ((y >= mPoint.y) && (y <= (mPoint.y + mWidth))) { 43 | return true; 44 | } 45 | } 46 | 47 | return false; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/entity/Player.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.entity; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Point; 5 | import android.util.Log; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * 玩家角色 12 | */ 13 | public class Player extends DynamicImage { 14 | 15 | private static final String TAG = "Player"; 16 | 17 | //角色状态 18 | public static final int STATE_MOVING = 1; 19 | public static final int STATE_STOP = 2; 20 | public static final int STATE_DIE = 3; 21 | //默认移动速度 22 | public static final int DEFAULT_SPEED = 3; 23 | //角色移动方向 24 | public static final int DIRECTION_UP = 1; 25 | public static final int DIRECTION_DOWN = 2; 26 | public static final int DIRECTION_LEFT = 3; 27 | public static final int DIRECTION_RIGHT = 4; 28 | //角色死亡 29 | public static final int PLAYER_DIE = 5; 30 | 31 | private int id = 0; 32 | //角色状态 33 | private int mState = 0; 34 | //角色移动速度 35 | private int mSpeed = DEFAULT_SPEED; 36 | //当前分数 37 | private int mScore = 0; 38 | //水平方向 39 | private int mDirection = 0; 40 | //上次移动方向 41 | private int mPreDirection = 0; 42 | //各个方向的图片 43 | private Map> mFrameBitmaps; 44 | 45 | public Player(Bitmap bitmap, Map> frameBitmaps, Point point) { 46 | super(bitmap, null, true, point); 47 | this.mFrameBitmaps = frameBitmaps; 48 | } 49 | 50 | public int getId() { 51 | return id; 52 | } 53 | 54 | public void setId(int id) { 55 | this.id = id; 56 | } 57 | 58 | public void addSpeed() { 59 | this.mSpeed++; 60 | } 61 | 62 | public int getSpeed() { 63 | return this.mSpeed; 64 | } 65 | 66 | public void addScore(int score) { 67 | this.mScore += score; 68 | } 69 | 70 | public int getScore() { 71 | return this.mScore; 72 | } 73 | 74 | public int getDirection() { 75 | return mDirection; 76 | } 77 | 78 | public void setDirection(int direction) { 79 | this.mDirection = direction; 80 | } 81 | 82 | public int getState() { 83 | return mState; 84 | } 85 | 86 | public void setState(int state) { 87 | this.mState = state; 88 | } 89 | 90 | public boolean isMoving() { 91 | return (this.mState == STATE_MOVING); 92 | } 93 | 94 | public boolean isAlive() { 95 | return (this.mState != STATE_DIE); 96 | } 97 | 98 | @Override 99 | public void doAnimation() { 100 | if (mDirection != 0 && mDirection != mPreDirection) { 101 | setFrameBitmap(mFrameBitmaps.get(mDirection)); 102 | mPreDirection = mDirection; 103 | } 104 | 105 | if (mState == STATE_DIE) { 106 | setFrameBitmap(mFrameBitmaps.get(PLAYER_DIE)); 107 | setLoop(false); 108 | } 109 | 110 | super.doAnimation(); 111 | } 112 | 113 | //获取左上角的标准位置 114 | public Point getStandardPoint() { 115 | Point point = getStandardMapPoint(); //获取标准点 116 | point.x *= mWidth; //乘以宽高 117 | point.y *= mHeight; 118 | return point; 119 | } 120 | 121 | //获取当前玩家的标准二维点 122 | public Point getStandardMapPoint() { 123 | int nowX = getX(); 124 | int nowY = getY(); 125 | 126 | Point mapPoint = new Point(); 127 | 128 | if (nowX != 0) { 129 | mapPoint.x = (int) ((float) nowX / mWidth + .5); 130 | 131 | } 132 | if (nowY != 0) { 133 | mapPoint.y = (int) ((float) nowY / mHeight + .5); 134 | } 135 | 136 | return mapPoint; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/entity/data/GameData.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.entity.data; 2 | 3 | /** 4 | * 游戏地图单元对象,获取自数据库 5 | */ 6 | 7 | public class GameData { 8 | 9 | private int id; 10 | private int mType; 11 | private int mSubType; 12 | private int mDrawable; 13 | private boolean mIsVisible = true; 14 | 15 | public GameData() { 16 | } 17 | 18 | public int getId() { 19 | return id; 20 | } 21 | 22 | public void setId(int id) { 23 | this.id = id; 24 | } 25 | 26 | public int getType() { 27 | return mType; 28 | } 29 | 30 | public void setType(int type) { 31 | mType = type; 32 | } 33 | 34 | public int getSubType() { 35 | return mSubType; 36 | } 37 | 38 | public void setSubType(int subType) { 39 | mSubType = subType; 40 | } 41 | 42 | public int getDrawable() { 43 | return mDrawable; 44 | } 45 | 46 | public void setDrawable(int drawable) { 47 | mDrawable = drawable; 48 | } 49 | 50 | public boolean isVisible() { 51 | return mIsVisible; 52 | } 53 | 54 | public void setVisible(boolean visible) { 55 | mIsVisible = visible; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/entity/data/GameLevelData.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.entity.data; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * 游戏关卡相关数据 7 | */ 8 | 9 | public class GameLevelData { 10 | private int id; 11 | private int stage; 12 | private String LevelTiles; 13 | private int playerStartX; 14 | private int playerStartY; 15 | 16 | public GameLevelData() { 17 | } 18 | 19 | public int getId() { 20 | return id; 21 | } 22 | 23 | public void setId(int id) { 24 | this.id = id; 25 | } 26 | 27 | public int getStage() { 28 | return stage; 29 | } 30 | 31 | public void setStage(int stage) { 32 | this.stage = stage; 33 | } 34 | 35 | public String getLevelTiles() { 36 | return LevelTiles; 37 | } 38 | 39 | public void setLevelTiles(String levelTiles) { 40 | LevelTiles = levelTiles; 41 | } 42 | 43 | public int getPlayerStartX() { 44 | return playerStartX; 45 | } 46 | 47 | public void setPlayerStartX(int playerStartX) { 48 | this.playerStartX = playerStartX; 49 | } 50 | 51 | public int getPlayerStartY() { 52 | return playerStartY; 53 | } 54 | 55 | public void setPlayerStartY(int playerStartY) { 56 | this.playerStartY = playerStartY; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/net/ConnectedService.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.net; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.os.HandlerThread; 6 | import android.os.Looper; 7 | import android.os.Message; 8 | import android.util.Log; 9 | 10 | import com.kuoruan.bomberman.util.ConnectConstants; 11 | import com.kuoruan.bomberman.util.DataUtil; 12 | import com.kuoruan.bomberman.util.GameConstants; 13 | 14 | import org.json.JSONException; 15 | import org.json.JSONObject; 16 | 17 | import java.io.BufferedReader; 18 | import java.io.IOException; 19 | import java.io.InputStream; 20 | import java.io.InputStreamReader; 21 | import java.io.OutputStream; 22 | import java.net.InetSocketAddress; 23 | import java.net.ServerSocket; 24 | import java.net.Socket; 25 | 26 | /** 27 | * 联机后数据传输 28 | */ 29 | public class ConnectedService { 30 | 31 | public static final String TAG = "ConnectedService"; 32 | private static final boolean DEBUG = true; 33 | 34 | private String mIp; 35 | 36 | private Socket mSocket; 37 | private GameReceiver mReceiver; 38 | private GameSender mSender; 39 | private boolean isServer; 40 | 41 | private static final int TCP_PORT = 8899; 42 | 43 | private Handler mRequestHandler; 44 | 45 | public ConnectedService(Handler handler, String ip, boolean isServer) { 46 | mRequestHandler = handler; 47 | this.isServer = isServer; 48 | this.mIp = ip; 49 | mReceiver = new GameReceiver(); 50 | mReceiver.start(); 51 | 52 | HandlerThread sendThread = new HandlerThread("GameSender"); 53 | sendThread.start(); 54 | mSender = new GameSender(sendThread.getLooper()); 55 | } 56 | 57 | public void stop() { 58 | mSender.quit(); 59 | mReceiver.quit(); 60 | } 61 | 62 | /** 63 | * 发送TCP数据 64 | * 65 | * @param jsonObject 66 | */ 67 | public void sendTCPData(JSONObject jsonObject) { 68 | byte[] data = DataUtil.JSONToTCPBytes(jsonObject); 69 | Message msg = Message.obtain(); 70 | msg.obj = data; 71 | mSender.sendMessage(msg); 72 | } 73 | 74 | /** 75 | * TCP消息接收线程 76 | */ 77 | class GameReceiver extends Thread { 78 | 79 | //byte[] buf = new byte[1024]; 80 | boolean isRunning = true; 81 | 82 | ServerSocket server; 83 | InputStream is; 84 | BufferedReader buf; 85 | 86 | public GameReceiver() { 87 | } 88 | 89 | @Override 90 | public void run() { 91 | try { 92 | if (isServer) { 93 | Log.i(TAG, "Connected: it is server"); 94 | server = new ServerSocket(TCP_PORT); 95 | mSocket = server.accept(); 96 | Log.d(TAG, "server:net connected"); 97 | mRequestHandler.sendEmptyMessage(ConnectConstants.GAME_CONNECTED); 98 | } else { 99 | Log.i(TAG, "Connected: it is client"); 100 | Socket s = new Socket(); 101 | InetSocketAddress address = new InetSocketAddress(mIp, TCP_PORT); 102 | /* 103 | * 连接失败尝试重连,重试8次 因为机器性能不一样不能保证作为Server端的Activity先于客户端启动 104 | */ 105 | int retryCount = 0; 106 | while (retryCount < 8) { 107 | try { 108 | s.connect(address); 109 | mSocket = s; 110 | mRequestHandler.sendEmptyMessage(ConnectConstants.GAME_CONNECTED); 111 | Log.d(TAG, "client:net connected"); 112 | break; 113 | } catch (IOException e) { 114 | retryCount++; 115 | s = new Socket(); 116 | try { 117 | Thread.sleep(200); 118 | } catch (InterruptedException e1) { 119 | Log.d(TAG, "thread exception : " + e1.getMessage()); 120 | } 121 | Log.d(TAG, "connect exception :" + e.getMessage() + " retry count=" + retryCount); 122 | } 123 | } 124 | if (retryCount >= 8) { 125 | return; 126 | } 127 | } 128 | } catch (IOException e) { 129 | Log.d(TAG, "socket exception:" + e.getMessage()); 130 | return; 131 | } 132 | 133 | try { 134 | is = mSocket.getInputStream(); 135 | buf = new BufferedReader(new InputStreamReader(is)); 136 | String data; 137 | while (isRunning) { 138 | if ((data = buf.readLine()) == null) { 139 | // 连接断开 140 | break; 141 | } 142 | if (DEBUG) { 143 | Log.d(TAG, "tcp received:" + data); 144 | } 145 | processNetData(data); 146 | } 147 | } catch (IOException e) { 148 | Log.d(TAG, "IOException: an error occurs while receiving data"); 149 | } 150 | } 151 | 152 | public void quit() { 153 | try { 154 | isRunning = false; 155 | if (is != null) { 156 | is.close(); 157 | } 158 | if (buf != null) { 159 | buf.close(); 160 | } 161 | if (mSocket != null) { 162 | mSocket.close(); 163 | } 164 | if (server != null) { 165 | server.close(); 166 | } 167 | } catch (IOException e) { 168 | Log.d(TAG, "close Socket Exception:" + e.getMessage()); 169 | } 170 | } 171 | 172 | } 173 | 174 | /** 175 | * 把消息交给TCP发送线程发送 176 | */ 177 | class GameSender extends Handler { 178 | 179 | public GameSender(Looper looper) { 180 | super(looper); 181 | } 182 | 183 | @Override 184 | public void handleMessage(Message msg) { 185 | byte[] data = (byte[]) msg.obj; 186 | try { 187 | if (mSocket == null) { 188 | Log.d(TAG, "Send fail,socket is null"); 189 | return; 190 | } 191 | OutputStream os = mSocket.getOutputStream(); 192 | // 发送数据 193 | os.write(data); 194 | os.flush(); 195 | } catch (IOException e) { 196 | Log.d(TAG, "tcp socket error:" + e.getMessage()); 197 | } 198 | 199 | } 200 | 201 | public void quit() { 202 | getLooper().quit(); 203 | } 204 | 205 | } 206 | 207 | // 处理消息 208 | private void processNetData(String data) { 209 | JSONObject jsonObject = null; 210 | int type = 0; 211 | try { 212 | jsonObject = new JSONObject(data); 213 | type = jsonObject.getInt(ConnectConstants.TYPE); 214 | } catch (JSONException e) { 215 | e.printStackTrace(); 216 | } 217 | 218 | switch (type) { 219 | case GameConstants.PLAYER_ADD: 220 | case GameConstants.PLAYER_MOVE: 221 | case GameConstants.PLAYER_STOP: 222 | case GameConstants.PLAYER_DIE: 223 | case GameConstants.SET_BOMB: 224 | notifyPlayerAction(type, jsonObject); 225 | break; 226 | default: 227 | break; 228 | } 229 | } 230 | 231 | private void notifyPlayerAction(int type, JSONObject jsonObject) { 232 | if (jsonObject == null) { 233 | return; 234 | } 235 | Message msg = Message.obtain(); 236 | msg.what = type; 237 | msg.obj = jsonObject; 238 | mRequestHandler.sendMessage(msg); 239 | } 240 | 241 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/net/ConnectingService.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.net; 2 | 3 | import android.os.Build; 4 | import android.os.Bundle; 5 | import android.os.Handler; 6 | import android.os.HandlerThread; 7 | import android.os.Looper; 8 | import android.os.Message; 9 | import android.util.Log; 10 | 11 | import com.kuoruan.bomberman.util.ConnectConstants; 12 | 13 | import org.json.JSONException; 14 | import org.json.JSONObject; 15 | 16 | import java.io.IOException; 17 | import java.io.OutputStream; 18 | import java.net.DatagramPacket; 19 | import java.net.DatagramSocket; 20 | import java.net.InetAddress; 21 | import java.net.MulticastSocket; 22 | import java.net.Socket; 23 | import java.net.SocketException; 24 | import java.net.UnknownHostException; 25 | import java.util.Arrays; 26 | 27 | /** 28 | * UDP联机管理
29 | * 初始化这个对象后,调用{@link #start()}方法就会搜索局域网 内的可连接手机,同时自己也会成为别人可见的对象。
30 | * 当搜索到可联机对象后会返回可连对象的机器名和IP地址。 31 | */ 32 | public class ConnectingService { 33 | public static final String TAG = "ConnectManager"; 34 | private static final boolean DEBUG = true; 35 | 36 | private String mIp; 37 | 38 | // UPD接收程序 39 | private DatagramSocket mDataSocket; 40 | // 点对多广播 41 | private MulticastSocket mMulticastSocket; 42 | private InetAddress mCastAddress; 43 | 44 | // 广播组地址 45 | private static final String MUL_IP = "230.0.2.2"; 46 | //多人游戏端口 47 | private static final int MUL_PORT = 1688; 48 | //UDP端口 49 | private static final int UDP_PORT = 2599; 50 | 51 | // 接收UPD消息 52 | private UDPReceiver mUDPReceiver; 53 | // 接收广播消息 54 | private MulticastReceiver mMulticastReceiver; 55 | // udp消息发送模块 56 | private UDPSendHandler mUDPSender; 57 | // 广播消息发送模块 58 | private MulticastSendHandler mBroadcastSender; 59 | 60 | private Handler mRequestHandler; 61 | 62 | public ConnectingService(String ip, Handler request) { 63 | this.mRequestHandler = request; 64 | this.mIp = ip; 65 | this.mUDPReceiver = new UDPReceiver(); 66 | this.mMulticastReceiver = new MulticastReceiver(); 67 | } 68 | 69 | /** 70 | * 启动连接程序 71 | */ 72 | public void start() { 73 | mUDPReceiver.start(); 74 | mMulticastReceiver.start(); 75 | 76 | HandlerThread udpThread = new HandlerThread("udpSender"); 77 | udpThread.start(); 78 | mUDPSender = new UDPSendHandler(udpThread.getLooper()); 79 | 80 | HandlerThread broadcastThread = new HandlerThread("broadcastSender"); 81 | broadcastThread.start(); 82 | mBroadcastSender = new MulticastSendHandler(broadcastThread.getLooper()); 83 | } 84 | 85 | public void stop() { 86 | mUDPReceiver.quit(); 87 | mMulticastReceiver.quit(); 88 | mUDPSender.getLooper().quit(); 89 | mBroadcastSender.getLooper().quit(); 90 | } 91 | 92 | /** 93 | * 发送一个查询广播消息,查询当前可连接对象 94 | */ 95 | public void sendScanMsg() { 96 | Message msg = Message.obtain(); 97 | byte[] buf = packageBroadcast(ConnectConstants.BROADCAST_JOIN); 98 | msg.obj = buf; 99 | mBroadcastSender.sendMessage(msg); 100 | } 101 | 102 | /** 103 | * 发送一个查询广播消息,退出可联机 104 | */ 105 | public void sendExitMsg() { 106 | // 起一个线程发送一个局域网广播(android主线程不能有网络操作) 107 | // 不用mMultiCastSocket对象发送时因为退出的时候涉及跨线程操作 108 | // 可能mMultiCastSocket已经close状态,不可控 109 | new Thread() { 110 | @Override 111 | public void run() { 112 | MulticastSocket multicastSocket; 113 | try { 114 | InetAddress address = InetAddress.getByName(MUL_IP); 115 | multicastSocket = new MulticastSocket(); 116 | multicastSocket.setTimeToLive(1); 117 | byte[] buf = packageBroadcast(ConnectConstants.BROADCAST_EXIT); 118 | DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length); 119 | // 接收地址和group的标识相同 120 | datagramPacket.setAddress(address); 121 | // 发送至的端口号 122 | datagramPacket.setPort(MUL_PORT); 123 | multicastSocket.send(datagramPacket); 124 | multicastSocket.close(); 125 | } catch (IOException e) { 126 | Log.d(TAG, "send exit multiCast fail:" + e.getMessage()); 127 | } 128 | } 129 | }.start(); 130 | } 131 | 132 | /** 133 | * 发送请求连接消息 134 | * 135 | * @param ipDst 136 | */ 137 | public void sendAskConnect(String ipDst) { 138 | Message msg = Message.obtain(); 139 | Bundle b = new Bundle(); 140 | b.putString("ipDst", ipDst); 141 | byte[] data = createAskConnect(); 142 | b.putByteArray("data", data); 143 | msg.setData(b); 144 | mUDPSender.sendMessage(msg); 145 | } 146 | 147 | 148 | /** 149 | * 同意联机 150 | */ 151 | public void accept(String ipDst) { 152 | Message msg = Message.obtain(); 153 | Bundle b = new Bundle(); 154 | b.putString("ipDst", ipDst); 155 | byte[] data = createConnectResponse(ConnectConstants.CONNECT_AGREE); 156 | b.putByteArray("data", data); 157 | msg.setData(b); 158 | mUDPSender.sendMessage(msg); 159 | } 160 | 161 | /** 162 | * 拒绝请求 163 | */ 164 | public void reject(String ipDst) { 165 | Message msg = Message.obtain(); 166 | Bundle b = new Bundle(); 167 | b.putString("ipDst", ipDst); 168 | byte[] data = createConnectResponse(ConnectConstants.CONNECT_REJECT); 169 | b.putByteArray("data", data); 170 | msg.setData(b); 171 | mUDPSender.sendMessage(msg); 172 | } 173 | 174 | /** 175 | * 接收UDP消息,未建立TCP连接之前,都通过udp接收消息 176 | * 177 | * @author qingc 178 | */ 179 | class UDPReceiver extends Thread { 180 | 181 | byte[] buf = new byte[1024]; 182 | boolean isRunning = true; 183 | 184 | private DatagramSocket dataSocket; 185 | private DatagramPacket dataPacket; 186 | 187 | public UDPReceiver() { 188 | try { 189 | dataSocket = new DatagramSocket(UDP_PORT); 190 | mDataSocket = dataSocket; 191 | dataPacket = new DatagramPacket(buf, buf.length); 192 | } catch (SocketException e) { 193 | isRunning = false; 194 | Log.d(TAG, "Socket Exception:" + e.getMessage()); 195 | } 196 | } 197 | 198 | @Override 199 | public void run() { 200 | try { 201 | while (isRunning) { 202 | mDataSocket.receive(dataPacket); 203 | String data = new String(dataPacket.getData(), 0, dataPacket.getLength()); 204 | Log.d(TAG, "udp receiver: " + data); 205 | processUDPReceive(data); 206 | } 207 | } catch (SocketException e) { 208 | isRunning = false; 209 | Log.d(TAG, "Socket Exception:" + e.getMessage()); 210 | } catch (IOException e) { 211 | isRunning = false; 212 | Log.d(TAG, "IOException: an error occurs while receiving the packet"); 213 | } 214 | } 215 | 216 | public void quit() { 217 | dataSocket.close(); 218 | isRunning = false; 219 | } 220 | 221 | } 222 | 223 | /** 224 | * 处理UDP接受数据 225 | * 226 | * @param data 接收到的Json字符串 227 | */ 228 | private void processUDPReceive(String data) { 229 | JSONObject jsonObject = null; 230 | int type = 0; 231 | try { 232 | jsonObject = new JSONObject(data); 233 | type = jsonObject.getInt(ConnectConstants.TYPE); 234 | } catch (JSONException e) { 235 | e.printStackTrace(); 236 | } 237 | 238 | switch (type) { 239 | case ConnectConstants.UDP_JOIN: 240 | processUDPJoin(jsonObject); 241 | break; 242 | case ConnectConstants.CONNECT_ASK: 243 | processConnectAsk(jsonObject); 244 | break; 245 | case ConnectConstants.CONNECT_AGREE: 246 | case ConnectConstants.CONNECT_REJECT: 247 | processConnectResponse(jsonObject); 248 | default: 249 | break; 250 | } 251 | } 252 | 253 | /** 254 | * 发送UDP消息,未建立TCP连接之前,都通过UDP发送指令到制定的ip 255 | */ 256 | class UDPSendHandler extends Handler { 257 | 258 | public UDPSendHandler(Looper looper) { 259 | super(looper); 260 | } 261 | 262 | @Override 263 | public void handleMessage(Message msg) { 264 | Bundle b = msg.peekData(); 265 | String ipDst = b.getString("ipDst"); 266 | byte[] data = b.getByteArray("data"); 267 | Log.d(TAG, "udp send destination ip:" + ipDst); 268 | if (DEBUG) { 269 | Log.d(TAG, "udp send data:" + Arrays.toString(data)); 270 | } 271 | if (data == null) { 272 | onError(ConnectConstants.UDP_DATA_ERROR); 273 | } 274 | try { 275 | DatagramSocket ds; 276 | ds = mDataSocket; 277 | if (ds == null) { 278 | onError(ConnectConstants.SOCKET_NULL); 279 | return; 280 | } 281 | InetAddress dstAddress = InetAddress.getByName(ipDst); 282 | 283 | // 创建发送数据包 284 | DatagramPacket dataPacket = new DatagramPacket(data, data.length, dstAddress, UDP_PORT); 285 | ds.send(dataPacket); 286 | } catch (UnknownHostException e1) { 287 | Log.d(TAG, "ip is not corrected"); 288 | onError(ConnectConstants.UDP_IP_ERROR); 289 | } catch (IOException e) { 290 | Log.d(TAG, "udp socket error:" + e.getMessage()); 291 | } 292 | 293 | } 294 | 295 | public void quit() { 296 | getLooper().quit(); 297 | } 298 | 299 | } 300 | 301 | /** 302 | * 接收广播消息线程,监听其他手机的扫描或加入广播 303 | */ 304 | class MulticastReceiver extends Thread { 305 | 306 | byte[] buffer = new byte[1024]; 307 | private boolean isRunning = true; 308 | 309 | private MulticastSocket multiSocket; 310 | private DatagramPacket dataPacket; 311 | 312 | public MulticastReceiver() { 313 | try { 314 | InetAddress address = InetAddress.getByName(MUL_IP); 315 | multiSocket = new MulticastSocket(); 316 | // 接收数据时需要指定监听的端口号 317 | multiSocket = new MulticastSocket(MUL_PORT); 318 | // 加入广播组 319 | mCastAddress = address; 320 | multiSocket.joinGroup(address); 321 | multiSocket.setTimeToLive(1); 322 | dataPacket = new DatagramPacket(buffer, buffer.length); 323 | // 全局引用指向这里的广播socket,用于发送广播消息 324 | mMulticastSocket = multiSocket; 325 | } catch (IOException e) { 326 | isRunning = false; 327 | Log.d(TAG, "Init multiCast fail by IOException=" + e.getMessage()); 328 | } 329 | } 330 | 331 | @Override 332 | public void run() { 333 | try { 334 | while (isRunning) { 335 | // 接收数据,会进入阻塞状态 336 | mMulticastSocket.receive(dataPacket); 337 | // 从buffer中截取收到的数据 338 | byte[] message = new byte[dataPacket.getLength()]; 339 | System.arraycopy(buffer, 0, message, 0, 340 | dataPacket.getLength()); 341 | Log.d(TAG, "multiCast receive:" + Arrays.toString(message)); 342 | String ip = processBroadcast(message); 343 | // Check ip address and send ip address myself to it. 344 | if (ip != null && !ip.equals(mIp)) { 345 | Message msg = Message.obtain(); 346 | Bundle b = new Bundle(); 347 | b.putString("ipDst", ip); 348 | byte[] data = packageUDPJoin(); 349 | b.putByteArray("data", data); 350 | msg.setData(b); 351 | mUDPSender.sendMessage(msg); 352 | } 353 | } 354 | } catch (IOException e) { 355 | Log.d(TAG, "IOException=" + e.getMessage()); 356 | } 357 | } 358 | 359 | public void quit() { 360 | // close socket 361 | multiSocket.close(); 362 | isRunning = false; 363 | } 364 | 365 | } 366 | 367 | /** 368 | * 发送广播消息 369 | */ 370 | class MulticastSendHandler extends Handler { 371 | 372 | public MulticastSendHandler(Looper looper) { 373 | super(looper); 374 | } 375 | 376 | @Override 377 | public void handleMessage(Message msg) { 378 | 379 | byte[] buf = (byte[]) msg.obj; 380 | if (DEBUG) { 381 | Log.d(TAG, "BroadcastSendHandler:data=" + buf); 382 | } 383 | MulticastSocket s = mMulticastSocket; 384 | if (s == null) { 385 | onError(ConnectConstants.SOCKET_NULL); 386 | return; 387 | } 388 | InetAddress address = mCastAddress; 389 | if (address == null || !address.isMulticastAddress()) { 390 | onError(ConnectConstants.MULTICAST_ERROR); 391 | return; 392 | } 393 | try { 394 | // s.setTimeToLive(1); is it nessary? 395 | DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length); 396 | // 设置发送group地址 397 | datagramPacket.setAddress(address); 398 | // 发送至的端口号 399 | datagramPacket.setPort(MUL_PORT); 400 | s.send(datagramPacket); 401 | } catch (IOException e) { 402 | Log.d(TAG, "send multicast fail:" + e.getMessage()); 403 | onError(ConnectConstants.SOCKET_NULL); 404 | } 405 | } 406 | 407 | public void quit() { 408 | getLooper().quit(); 409 | } 410 | } 411 | 412 | /** 413 | * 错误信息 414 | * 415 | * @param error 416 | */ 417 | private void onError(int error) { 418 | Log.d(TAG, "error:" + error); 419 | Message msg = Message.obtain(); 420 | msg.what = error; 421 | mRequestHandler.sendMessage(msg); 422 | } 423 | 424 | /** 425 | * 有新的可联机对象加入 426 | * 427 | * @param name 机器名 428 | * @param ip 地址 429 | */ 430 | private void onJoin(String name, String ip) { 431 | Message msg = Message.obtain(); 432 | msg.what = ConnectConstants.ON_JOIN; 433 | Bundle b = new Bundle(); 434 | b.putString("name", name); 435 | b.putString("ip", ip); 436 | msg.setData(b); 437 | mRequestHandler.sendMessage(msg); 438 | } 439 | 440 | /** 441 | * 有可联机对象退出 442 | * 443 | * @param name 机器名 444 | * @param ip 地址 445 | */ 446 | private void onExit(String name, String ip) { 447 | Message msg = Message.obtain(); 448 | msg.what = ConnectConstants.ON_EXIT; 449 | Bundle b = new Bundle(); 450 | b.putString("name", name); 451 | b.putString("ip", ip); 452 | msg.setData(b); 453 | mRequestHandler.sendMessage(msg); 454 | } 455 | 456 | /** 457 | * 处理加入可连接对象消息 458 | * 459 | * @param jsonObject 接收到Json对象 460 | */ 461 | private void processUDPJoin(JSONObject jsonObject) { 462 | String name = null; 463 | String ip = null; 464 | try { 465 | name = jsonObject.getString(ConnectConstants.HOST_NAME); 466 | ip = jsonObject.getString(ConnectConstants.IP); 467 | } catch (JSONException e) { 468 | e.printStackTrace(); 469 | } 470 | //处理加入用户 471 | onJoin(name, ip); 472 | } 473 | 474 | /** 475 | * 将本机名称和ip地址封装成Json发送 476 | * 477 | * @return 478 | */ 479 | private byte[] packageUDPJoin() { 480 | JSONObject jsonObject = new JSONObject(); 481 | try { 482 | jsonObject.put(ConnectConstants.HOST_NAME, Build.BRAND + "-" + Build.MODEL); 483 | jsonObject.put(ConnectConstants.IP, mIp); 484 | } catch (JSONException e) { 485 | e.printStackTrace(); 486 | } 487 | return jsonObject.toString().getBytes(); 488 | } 489 | 490 | /** 491 | * 处理广播消息 492 | * 493 | * @param data 接收到的消息体 494 | * @return 返回解析到的ip地址 495 | */ 496 | private String processBroadcast(byte[] data) { 497 | String json = new String(data); 498 | String ip = null; 499 | String name = null; 500 | int type = 0; 501 | try { 502 | JSONObject jsonObject = new JSONObject(json); 503 | ip = jsonObject.getString(ConnectConstants.IP); 504 | name = jsonObject.getString(ConnectConstants.HOST_NAME); 505 | type = jsonObject.getInt(ConnectConstants.TYPE); 506 | } catch (JSONException e) { 507 | e.printStackTrace(); 508 | } 509 | Log.d(TAG, "processBroadcast-->" + "name=" + name + " ip=" + ip); 510 | // 如果是自己发送的信息,则不加入可连接集合 511 | if (mIp.equals(ip)) { 512 | return ip; 513 | } 514 | if (type == ConnectConstants.BROADCAST_JOIN) { 515 | onJoin(name, ip); 516 | } else if (type == ConnectConstants.BROADCAST_EXIT) { 517 | onExit(name, ip); 518 | } 519 | return ip; 520 | } 521 | 522 | /** 523 | * 将本机名称和ip地址封装成byte数组 524 | * 525 | * @return 526 | */ 527 | private byte[] packageBroadcast(int type) { 528 | JSONObject jsonObject = new JSONObject(); 529 | try { 530 | jsonObject.put(ConnectConstants.IP, mIp); 531 | jsonObject.put(ConnectConstants.HOST_NAME, Build.BRAND + "-" + Build.MODEL); 532 | jsonObject.put(ConnectConstants.TYPE, type); 533 | } catch (JSONException e) { 534 | e.printStackTrace(); 535 | } 536 | return jsonObject.toString().getBytes(); 537 | } 538 | 539 | /** 540 | * 封装请求连接消息体 541 | * 542 | * @return 543 | */ 544 | private byte[] createAskConnect() { 545 | JSONObject jsonObject = new JSONObject(); 546 | try { 547 | jsonObject.put(ConnectConstants.IP, mIp); 548 | jsonObject.put(ConnectConstants.HOST_NAME, Build.BRAND + "-" + Build.MODEL); 549 | jsonObject.put(ConnectConstants.TYPE, ConnectConstants.CONNECT_ASK); 550 | } catch (JSONException e) { 551 | e.printStackTrace(); 552 | } 553 | 554 | return jsonObject.toString().getBytes(); 555 | } 556 | 557 | /** 558 | * 解析请求联机数据 559 | * 560 | * @param jsonObject 561 | */ 562 | private void processConnectAsk(JSONObject jsonObject) { 563 | String ip = null; 564 | String name = null; 565 | int type = 0; 566 | 567 | try { 568 | ip = jsonObject.getString(ConnectConstants.IP); 569 | name = jsonObject.getString(ConnectConstants.HOST_NAME); 570 | type = jsonObject.getInt(ConnectConstants.TYPE); 571 | } catch (JSONException e) { 572 | e.printStackTrace(); 573 | } 574 | 575 | Log.d(TAG, "processUDPJoin-->" + "name=" + name + " ip=" + ip); 576 | Message msg = Message.obtain(); 577 | msg.what = type; 578 | Bundle b = new Bundle(); 579 | b.putString("name", name); 580 | b.putString("ip", ip); 581 | msg.setData(b); 582 | mRequestHandler.sendMessage(msg); 583 | } 584 | 585 | /** 586 | * 创建连接相应消息 587 | * 588 | * @param type 589 | * @return 消息数组 590 | */ 591 | private byte[] createConnectResponse(int type) { 592 | JSONObject jsonObject = new JSONObject(); 593 | try { 594 | jsonObject.put(ConnectConstants.IP, mIp); 595 | jsonObject.put(ConnectConstants.HOST_NAME, Build.BOARD); 596 | jsonObject.put(ConnectConstants.TYPE, type); 597 | } catch (JSONException e) { 598 | e.printStackTrace(); 599 | } 600 | 601 | return jsonObject.toString().getBytes(); 602 | } 603 | 604 | /** 605 | * 解析连接请求响应并处理 606 | * 607 | * @param jsonObject 608 | */ 609 | private void processConnectResponse(JSONObject jsonObject) { 610 | String ip = null; 611 | String name = null; 612 | int type = 0; 613 | try { 614 | ip = jsonObject.getString(ConnectConstants.IP); 615 | name = jsonObject.getString(ConnectConstants.HOST_NAME); 616 | type = jsonObject.getInt(ConnectConstants.TYPE); 617 | } catch (JSONException e) { 618 | e.printStackTrace(); 619 | } 620 | 621 | Log.d(TAG, "processConnectResponse-->" + "name=" + name + " ip=" + ip); 622 | Message msg = Message.obtain(); 623 | msg.what = type; 624 | Bundle b = new Bundle(); 625 | b.putString("name", name); 626 | b.putString("ip", ip); 627 | msg.setData(b); 628 | mRequestHandler.sendMessage(msg); 629 | } 630 | 631 | } 632 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/util/BitmapManager.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.util; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * Created by Liao on 2016/5/5 0005. 13 | */ 14 | public class BitmapManager { 15 | 16 | private static Map mBitmapMaps = new HashMap<>(); 17 | 18 | public static Bitmap setAndGetBitmap(Context context, int resourceId) { 19 | if (!mBitmapMaps.containsKey(resourceId)) { 20 | BitmapFactory.Options opts = new BitmapFactory.Options(); 21 | opts.inJustDecodeBounds = true; 22 | Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId); 23 | 24 | if (bitmap != null) { 25 | mBitmapMaps.put(resourceId, bitmap); 26 | } 27 | } 28 | 29 | return mBitmapMaps.get(resourceId); 30 | } 31 | 32 | public static Map getBitmapMaps() { 33 | return mBitmapMaps; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/util/ConnectConstants.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.util; 2 | 3 | public class ConnectConstants { 4 | 5 | public static final int SOCKET_NULL = 0; 6 | public static final int IP_NULL = 1; 7 | public static final int ON_JOIN = 2; 8 | public static final int ON_EXIT = 3; 9 | public static final int UDP_IP_ERROR = 4; 10 | public static final int UDP_DATA_ERROR = 5; 11 | public static final int MULTICAST_ERROR = 6; 12 | 13 | public static final int CONNECT_ASK = 11; 14 | public static final int CONNECT_AGREE = 12; 15 | public static final int CONNECT_REJECT = 13; 16 | 17 | public static final int BROADCAST_JOIN = 0; 18 | public static final int BROADCAST_EXIT = 1; 19 | 20 | public static final int UDP_JOIN = 0; 21 | 22 | public static final int GAME_CONNECTED = 11; 23 | 24 | public static final String HOST_NAME = "hostName"; 25 | public static final String IP = "ip"; 26 | public static final String TYPE = "type"; 27 | public static final String PLAYER_ID = "playerId"; 28 | public static final String POINT_X = "x"; 29 | public static final String POINT_Y = "y"; 30 | public static final String DIRECTION = "direction"; 31 | public static final String MAP_X = "mapX"; 32 | public static final String MAP_Y = "mapY"; 33 | public static final String BOMB_TYPE = "bombType"; 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/util/DataUtil.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.util; 2 | 3 | import android.os.Bundle; 4 | 5 | import org.json.JSONArray; 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | import java.io.ByteArrayOutputStream; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.lang.reflect.Array; 13 | import java.util.Collection; 14 | import java.util.Map; 15 | import java.util.Set; 16 | 17 | /** 18 | * 各种数据工具类 19 | */ 20 | public class DataUtil { 21 | 22 | public static String streamToString(InputStream is) { 23 | byte[] bytes = new byte[1024]; 24 | int length = 0; 25 | ByteArrayOutputStream os = new ByteArrayOutputStream(); 26 | try { 27 | while ((length = is.read(bytes)) != -1) { 28 | os.write(bytes, 0, length); 29 | } 30 | os.flush(); 31 | String result = new String(os.toByteArray(), "UTF-8"); 32 | return result; 33 | } catch (IOException e) { 34 | e.printStackTrace(); 35 | } 36 | return ""; 37 | } 38 | 39 | public static byte[] JSONToTCPBytes(JSONObject jsonObject) { 40 | String data = jsonObject.toString() + "\n"; 41 | return data.getBytes(); 42 | } 43 | 44 | public static JSONObject bundleToJSONObj(Bundle bundle) { 45 | JSONObject json = new JSONObject(); 46 | Set keys = bundle.keySet(); 47 | for (String key : keys) { 48 | try { 49 | // json.put(key, bundle.get(key)); see edit below 50 | json.put(key, wrap(bundle.get(key))); 51 | } catch (JSONException e) { 52 | e.printStackTrace(); 53 | } 54 | } 55 | 56 | return json; 57 | } 58 | 59 | public static Object wrap(Object o) { 60 | if (o == null) { 61 | return JSONObject.NULL; 62 | } 63 | if (o instanceof JSONArray || o instanceof JSONObject) { 64 | return o; 65 | } 66 | if (o.equals(JSONObject.NULL)) { 67 | return o; 68 | } 69 | try { 70 | if (o instanceof Collection) { 71 | return new JSONArray((Collection) o); 72 | } else if (o.getClass().isArray()) { 73 | return toJSONArray(o); 74 | } 75 | if (o instanceof Map) { 76 | return new JSONObject((Map) o); 77 | } 78 | if (o instanceof Boolean || 79 | o instanceof Byte || 80 | o instanceof Character || 81 | o instanceof Double || 82 | o instanceof Float || 83 | o instanceof Integer || 84 | o instanceof Long || 85 | o instanceof Short || 86 | o instanceof String) { 87 | return o; 88 | } 89 | if (o.getClass().getPackage().getName().startsWith("java.")) { 90 | return o.toString(); 91 | } 92 | } catch (Exception ignored) { 93 | } 94 | return null; 95 | } 96 | 97 | private static Object toJSONArray(Object array) throws JSONException { 98 | JSONArray result = new JSONArray(); 99 | if (!array.getClass().isArray()) { 100 | throw new JSONException("Not a primitive array: " + array.getClass()); 101 | } 102 | final int length = Array.getLength(array); 103 | for (int i = 0; i < length; ++i) { 104 | result.put(wrap(Array.get(array, i))); 105 | } 106 | return result; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/util/GameConstants.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.util; 2 | 3 | public class GameConstants { 4 | 5 | //多人游戏 6 | public static final int MULTI_PLAYER_STAGE = 0; 7 | /** 8 | * 默认炸弹数量 9 | */ 10 | public static final int DEFAULT_BOMB_COUNT = 2; 11 | public static final int GAME_OVER = 0; 12 | public static final int PLAYER_ADD = 1; 13 | public static final int PLAYER_MOVE = 2; 14 | public static final int PLAYER_STOP = 3; 15 | public static final int PLAYER_DIE = 4; 16 | public static final int SET_BOMB = 5; 17 | 18 | public static final int MODE_SINGLE = 0; 19 | public static final int MODE_FIGHT = 1; 20 | public static final int MODE_NET = 2; 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/util/PlayerManager.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.util; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.Point; 6 | import android.os.Handler; 7 | import android.os.Message; 8 | 9 | import com.kuoruan.bomberman.dao.GameDataDao; 10 | import com.kuoruan.bomberman.entity.Player; 11 | import com.kuoruan.bomberman.entity.data.GameData; 12 | 13 | import org.json.JSONException; 14 | import org.json.JSONObject; 15 | 16 | import java.util.ArrayList; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | /** 22 | * Created by Window10 on 2016/5/3. 23 | */ 24 | public class PlayerManager { 25 | private static final String TAG = "PlayerManager"; 26 | public static int mId = 1; 27 | private GameDataDao mGameDataDao; 28 | 29 | private Map mPlayers = new HashMap<>(); 30 | private Player mMyPlayer = null; 31 | private Map mPlayerData = null; 32 | private Map>> mPlayerTemplates = new HashMap<>(); 33 | private static final Point[] positions = {new Point(1, 1), new Point(15, 11), new Point(1, 11), new Point(15, 1)}; 34 | 35 | private Context mContext; 36 | private Handler mHandler; 37 | 38 | public PlayerManager(Context context, Handler handler, int id) { 39 | mContext = context; 40 | mHandler = handler; 41 | mId = id; 42 | mGameDataDao = GameDataDao.getInstance(context); 43 | mPlayerData = mGameDataDao.getPlayerData(); 44 | prepareMyPlayer(); 45 | } 46 | 47 | /** 48 | * 初始化玩家 49 | * 50 | * @return 51 | */ 52 | public void prepareMyPlayer() { 53 | Point mapPoint = positions[mId]; 54 | addPlayer(mId, mapPoint); 55 | mMyPlayer = mPlayers.get(mId); 56 | noticeMyPlayer(); 57 | } 58 | 59 | /** 60 | * 添加一个新的玩家 61 | */ 62 | public void addPlayer(int id, Point mapPoint) { 63 | Bitmap bitmap = getFirstBitmap(id); 64 | mapPoint.x *= bitmap.getWidth(); 65 | mapPoint.y = bitmap.getHeight(); 66 | Player player = new Player(bitmap, mPlayerTemplates.get(id), mapPoint); 67 | player.setId(id); 68 | mPlayers.put(id, player); 69 | } 70 | 71 | private Bitmap getFirstBitmap(int id) { 72 | Map> playerTemplate = setAndGetPlayerTemplates(id); 73 | return playerTemplate.get(Player.DIRECTION_DOWN).get(0); 74 | } 75 | 76 | /** 77 | * 准备玩家图片模板 78 | * 79 | * @param id 80 | */ 81 | private Map> setAndGetPlayerTemplates(int id) { 82 | if (!mPlayerTemplates.containsKey(id)) { 83 | GameData data = mPlayerData.get(id); 84 | 85 | Bitmap baseBitmap = BitmapManager.setAndGetBitmap(mContext, data.getDrawable()); 86 | 87 | int width = baseBitmap.getWidth() / 3; 88 | int height = baseBitmap.getHeight() / 7; 89 | 90 | Map> map = new HashMap<>(); 91 | for (int y = 0; y < 4; y++) { 92 | List list = new ArrayList<>(); 93 | for (int x = 0; x < 3; x++) { 94 | Bitmap newBitmap = Bitmap.createBitmap(baseBitmap, x * width, y * height, width, height); 95 | list.add(newBitmap); 96 | } 97 | 98 | map.put(y + 1, list); 99 | } 100 | 101 | List dieBitmaps = new ArrayList<>(); 102 | for (int y = 4; y < 7; y++) { 103 | for (int x = 0; x < 3; x++) { 104 | Bitmap newBitmap = Bitmap.createBitmap(baseBitmap, x * width, y * height, width, height); 105 | dieBitmaps.add(newBitmap); 106 | } 107 | } 108 | 109 | map.put(Player.PLAYER_DIE, dieBitmaps); 110 | mPlayerTemplates.put(id, map); 111 | } 112 | 113 | return mPlayerTemplates.get(id); 114 | } 115 | 116 | public Map getPlayers() { 117 | return mPlayers; 118 | } 119 | 120 | public int getPlayerCount() { 121 | return mPlayers.size(); 122 | } 123 | 124 | public void handlePlayerMove(JSONObject jsonObject) { 125 | int pid = 0; 126 | int direction = 0; 127 | int x = 0; 128 | int y = 0; 129 | try { 130 | pid = jsonObject.getInt(ConnectConstants.PLAYER_ID); 131 | direction = jsonObject.getInt(ConnectConstants.DIRECTION); 132 | x = jsonObject.getInt(ConnectConstants.POINT_X); 133 | y = jsonObject.getInt(ConnectConstants.POINT_Y); 134 | } catch (JSONException e) { 135 | e.printStackTrace(); 136 | } 137 | 138 | Player netPlayer = mPlayers.get(pid); 139 | netPlayer.setX(x); 140 | netPlayer.setY(y); 141 | netPlayer.setDirection(direction); 142 | netPlayer.setState(Player.STATE_MOVING); 143 | } 144 | 145 | public Player getMyPlayer() { 146 | return mMyPlayer; 147 | } 148 | 149 | public void handlePlayerAdd(JSONObject jsonObject) { 150 | int pid = 0; 151 | int x = 0; 152 | int y = 0; 153 | try { 154 | pid = jsonObject.getInt(ConnectConstants.PLAYER_ID); 155 | x = jsonObject.getInt(ConnectConstants.POINT_X); 156 | y = jsonObject.getInt(ConnectConstants.POINT_Y); 157 | } catch (JSONException e) { 158 | e.printStackTrace(); 159 | } 160 | if (mPlayers.containsKey(pid)) { 161 | return; 162 | } 163 | 164 | addPlayer(pid, new Point(x, y)); 165 | } 166 | 167 | public void handlePlayerStop(JSONObject jsonObject) { 168 | int pid = 0; 169 | try { 170 | pid = jsonObject.getInt(ConnectConstants.PLAYER_ID); 171 | } catch (JSONException e) { 172 | e.printStackTrace(); 173 | } 174 | if (pid == mId) { 175 | return; 176 | } 177 | Player netPlayer = mPlayers.get(pid); 178 | netPlayer.setState(Player.STATE_STOP); 179 | netPlayer.setDirection(0); 180 | } 181 | 182 | public void handlePlayerDie(JSONObject jsonObject) { 183 | int pid = 0; 184 | try { 185 | pid = jsonObject.getInt(ConnectConstants.PLAYER_ID); 186 | } catch (JSONException e) { 187 | e.printStackTrace(); 188 | } 189 | if (pid == mId) { 190 | return; 191 | } 192 | Player netPlayer = mPlayers.get(pid); 193 | netPlayer.setState(Player.STATE_DIE); 194 | netPlayer.setDirection(0); 195 | } 196 | 197 | public void noticeMyMove() { 198 | if (!SceneManager.isMultiStage()) return; 199 | 200 | Message msg = Message.obtain(); 201 | msg.what = GameConstants.PLAYER_MOVE; 202 | 203 | JSONObject jsonObject = new JSONObject(); 204 | try { 205 | jsonObject.put(ConnectConstants.TYPE, GameConstants.PLAYER_MOVE); 206 | jsonObject.put(ConnectConstants.PLAYER_ID, mId); 207 | jsonObject.put(ConnectConstants.DIRECTION, mMyPlayer.getDirection()); 208 | jsonObject.put(ConnectConstants.POINT_X, mMyPlayer.getX()); 209 | jsonObject.put(ConnectConstants.POINT_Y, mMyPlayer.getY()); 210 | } catch (JSONException e) { 211 | e.printStackTrace(); 212 | } 213 | 214 | msg.obj = jsonObject; 215 | mHandler.sendMessage(msg); 216 | } 217 | 218 | 219 | private void noticeMyPlayer() { 220 | if (!SceneManager.isMultiStage()) return; 221 | 222 | Message msg = Message.obtain(); 223 | msg.what = GameConstants.PLAYER_ADD; 224 | 225 | JSONObject jsonObject = new JSONObject(); 226 | try { 227 | jsonObject.put(ConnectConstants.TYPE, GameConstants.PLAYER_ADD); 228 | jsonObject.put(ConnectConstants.PLAYER_ID, mId); 229 | jsonObject.put(ConnectConstants.POINT_X, mMyPlayer.getX()); 230 | jsonObject.put(ConnectConstants.POINT_Y, mMyPlayer.getY()); 231 | } catch (JSONException e) { 232 | e.printStackTrace(); 233 | } 234 | 235 | msg.obj = jsonObject; 236 | mHandler.sendMessage(msg); 237 | } 238 | 239 | public void noticeMyStop() { 240 | if (!SceneManager.isMultiStage()) return; 241 | 242 | Message msg = Message.obtain(); 243 | msg.what = GameConstants.PLAYER_STOP; 244 | 245 | JSONObject jsonObject = new JSONObject(); 246 | try { 247 | jsonObject.put(ConnectConstants.TYPE, GameConstants.PLAYER_STOP); 248 | jsonObject.put(ConnectConstants.PLAYER_ID, mId); 249 | } catch (JSONException e) { 250 | e.printStackTrace(); 251 | } 252 | msg.obj = jsonObject; 253 | mHandler.sendMessage(msg); 254 | } 255 | 256 | public void noticeMyDie() { 257 | mMyPlayer.setState(Player.STATE_DIE); 258 | 259 | Message msg = Message.obtain(); 260 | msg.what = GameConstants.PLAYER_DIE; 261 | 262 | JSONObject jsonObject = new JSONObject(); 263 | try { 264 | jsonObject.put(ConnectConstants.TYPE, GameConstants.PLAYER_DIE); 265 | jsonObject.put(ConnectConstants.PLAYER_ID, mId); 266 | } catch (JSONException e) { 267 | e.printStackTrace(); 268 | } 269 | msg.obj = jsonObject; 270 | mHandler.sendMessage(msg); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/util/SPUtils.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.util; 2 | 3 | /** 4 | * Created by Liao on 2016/6/5 0005. 5 | */ 6 | 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.lang.reflect.Method; 9 | import java.util.Map; 10 | 11 | import android.content.Context; 12 | import android.content.SharedPreferences; 13 | 14 | public class SPUtils { 15 | /** 16 | * 保存在手机里面的文件名 17 | */ 18 | public static final String FILE_NAME = "share_data"; 19 | 20 | /** 21 | * 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法 22 | * 23 | * @param context 24 | * @param key 25 | * @param object 26 | */ 27 | public static void put(Context context, String key, Object object) { 28 | 29 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE); 30 | SharedPreferences.Editor editor = sp.edit(); 31 | 32 | if (object instanceof String) { 33 | editor.putString(key, (String) object); 34 | } else if (object instanceof Integer) { 35 | editor.putInt(key, (Integer) object); 36 | } else if (object instanceof Boolean) { 37 | editor.putBoolean(key, (Boolean) object); 38 | } else if (object instanceof Float) { 39 | editor.putFloat(key, (Float) object); 40 | } else if (object instanceof Long) { 41 | editor.putLong(key, (Long) object); 42 | } else { 43 | editor.putString(key, object.toString()); 44 | } 45 | 46 | SharedPreferencesCompat.apply(editor); 47 | } 48 | 49 | /** 50 | * 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值 51 | * 52 | * @param context 53 | * @param key 54 | * @param defaultObject 55 | * @return 56 | */ 57 | public static Object get(Context context, String key, Object defaultObject) { 58 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, 59 | Context.MODE_PRIVATE); 60 | 61 | if (defaultObject instanceof String) { 62 | return sp.getString(key, (String) defaultObject); 63 | } else if (defaultObject instanceof Integer) { 64 | return sp.getInt(key, (Integer) defaultObject); 65 | } else if (defaultObject instanceof Boolean) { 66 | return sp.getBoolean(key, (Boolean) defaultObject); 67 | } else if (defaultObject instanceof Float) { 68 | return sp.getFloat(key, (Float) defaultObject); 69 | } else if (defaultObject instanceof Long) { 70 | return sp.getLong(key, (Long) defaultObject); 71 | } 72 | 73 | return null; 74 | } 75 | 76 | /** 77 | * 移除某个key值已经对应的值 78 | * 79 | * @param context 80 | * @param key 81 | */ 82 | public static void remove(Context context, String key) { 83 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE); 84 | SharedPreferences.Editor editor = sp.edit(); 85 | editor.remove(key); 86 | SharedPreferencesCompat.apply(editor); 87 | } 88 | 89 | /** 90 | * 清除所有数据 91 | * 92 | * @param context 93 | */ 94 | public static void clear(Context context) { 95 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE); 96 | SharedPreferences.Editor editor = sp.edit(); 97 | editor.clear(); 98 | SharedPreferencesCompat.apply(editor); 99 | } 100 | 101 | /** 102 | * 查询某个key是否已经存在 103 | * 104 | * @param context 105 | * @param key 106 | * @return 107 | */ 108 | public static boolean contains(Context context, String key) { 109 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE); 110 | return sp.contains(key); 111 | } 112 | 113 | /** 114 | * 返回所有的键值对 115 | * 116 | * @param context 117 | * @return 118 | */ 119 | public static Map getAll(Context context) { 120 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE); 121 | return sp.getAll(); 122 | } 123 | 124 | /** 125 | * 创建一个解决SharedPreferencesCompat.apply方法的一个兼容类 126 | * 127 | * @author zhy 128 | */ 129 | private static class SharedPreferencesCompat { 130 | private static final Method sApplyMethod = findApplyMethod(); 131 | 132 | /** 133 | * 反射查找apply的方法 134 | * 135 | * @return 136 | */ 137 | private static Method findApplyMethod() { 138 | try { 139 | Class clazz = SharedPreferences.Editor.class; 140 | return clazz.getMethod("apply"); 141 | } catch (NoSuchMethodException e) { 142 | } 143 | 144 | return null; 145 | } 146 | 147 | /** 148 | * 如果找到则使用apply执行,否则使用commit 149 | * 150 | * @param editor 151 | */ 152 | public static void apply(SharedPreferences.Editor editor) { 153 | try { 154 | if (sApplyMethod != null) { 155 | sApplyMethod.invoke(editor); 156 | return; 157 | } 158 | } catch (Exception e) { 159 | e.printStackTrace(); 160 | } 161 | editor.commit(); 162 | } 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/util/SceneManager.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.util; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.Point; 6 | import android.os.Handler; 7 | import android.os.Message; 8 | import android.util.Log; 9 | 10 | import com.kuoruan.bomberman.dao.GameDataDao; 11 | import com.kuoruan.bomberman.dao.GameLevelDataDao; 12 | import com.kuoruan.bomberman.entity.Bomb; 13 | import com.kuoruan.bomberman.entity.BombFire; 14 | import com.kuoruan.bomberman.entity.Collision; 15 | import com.kuoruan.bomberman.entity.GameTile; 16 | import com.kuoruan.bomberman.entity.Player; 17 | import com.kuoruan.bomberman.entity.data.GameData; 18 | import com.kuoruan.bomberman.entity.data.GameLevelData; 19 | 20 | import org.json.JSONException; 21 | import org.json.JSONObject; 22 | 23 | import java.util.ArrayList; 24 | import java.util.HashMap; 25 | import java.util.List; 26 | import java.util.Map; 27 | 28 | /** 29 | * Created by Liao on 2016/5/8 0008. 30 | */ 31 | public class SceneManager { 32 | 33 | private final String TAG = "SceneManager"; 34 | 35 | public static int sceneXMax = 0; 36 | public static int sceneYMax = 0; 37 | 38 | public static int tileWidth = 0; 39 | public static int tileHeight = 0; 40 | 41 | public static int mTotalBombCount = GameConstants.DEFAULT_BOMB_COUNT; 42 | public static int gameStage = 0; 43 | 44 | private int mPlayerBombCount = 0; 45 | private int mBombType = Bomb.NORMAL; 46 | private int mFireType = BombFire.NORMAL; 47 | 48 | private Map mTileData = null; 49 | private Map mBombData = null; 50 | private Map mFireData = null; 51 | 52 | private Map> mBombTemplates = new HashMap<>(); 53 | private Map>> mFireTemplates = new HashMap<>(); 54 | 55 | //地图上所有的砖块 56 | private GameTile[][] mGameTiles = null; 57 | private List mBombFires = new ArrayList<>(); 58 | //玩家周围的砖块 59 | private List mSurroundTiles = new ArrayList<>(); 60 | private Context mContext; 61 | private Handler mHandler; 62 | 63 | private GameDataDao mGameDataDao; 64 | private GameLevelDataDao mGameLevelDataDao; 65 | private GameLevelData mGameLevelData; 66 | 67 | public SceneManager(Context context, Handler handler, int stage) { 68 | mContext = context; 69 | mHandler = handler; 70 | gameStage = stage; 71 | mGameDataDao = GameDataDao.getInstance(context); 72 | this.mTileData = mGameDataDao.getGameTileData(); 73 | this.mBombData = mGameDataDao.getBombData(); 74 | this.mFireData = mGameDataDao.getFireData(); 75 | mGameLevelDataDao = GameLevelDataDao.getInstance(context); 76 | } 77 | 78 | /** 79 | * 处理地图数据 80 | */ 81 | public GameTile[][] parseGameTileData() { 82 | mGameLevelData = mGameLevelDataDao.getGameLevelData(gameStage); 83 | String levelTileData = mGameLevelData.getLevelTiles(); 84 | 85 | if (levelTileData == null) { 86 | return null; 87 | } 88 | 89 | Bitmap bitmap; 90 | Point tilePoint = new Point(); 91 | int tileX = 0; 92 | int tileY = 0; 93 | 94 | String[] tileLines = levelTileData.split(GameLevelDataDao.TILE_DATA_LINE_BREAK); 95 | int rows = tileLines.length; 96 | int cols = 0; 97 | 98 | for (int i = 0; i < rows; i++) { 99 | String[] tiles = tileLines[i].split(","); 100 | 101 | //如果没有列数目 102 | if (cols == 0 && mGameTiles == null) { 103 | cols = tiles.length; 104 | mGameTiles = new GameTile[rows][cols]; 105 | } 106 | 107 | for (int j = 0; j < cols; j++) { 108 | int tileNum = Integer.parseInt(tiles[j]); 109 | GameData gameTileData = mTileData.get(tileNum); 110 | 111 | if ((mGameTiles.length > 0) && (gameTileData != null)) { 112 | tilePoint.x = tileX; 113 | tilePoint.y = tileY; 114 | 115 | bitmap = BitmapManager.setAndGetBitmap(mContext, gameTileData.getDrawable()); 116 | GameTile gameTile = new GameTile(bitmap, tilePoint, gameTileData.getSubType()); 117 | gameTile.setVisible(gameTileData.isVisible()); 118 | 119 | if (tileWidth == 0) { 120 | tileWidth = gameTile.getWidth(); 121 | } 122 | if (tileHeight == 0) { 123 | tileHeight = gameTile.getHeight(); 124 | } 125 | 126 | if (sceneXMax == 0 && cols > 0) { 127 | sceneXMax = cols * tileWidth; 128 | } 129 | if (sceneYMax == 0 && rows > 0) { 130 | sceneYMax = rows * tileHeight; 131 | } 132 | 133 | mGameTiles[i][j] = gameTile; 134 | } 135 | 136 | tileX += tileWidth; 137 | } 138 | tileX = 0; 139 | tileY += tileHeight; 140 | } 141 | 142 | return mGameTiles; 143 | } 144 | 145 | /** 146 | * 获取炸弹模版 147 | * 148 | * @param bombType 149 | */ 150 | public List setAndGetBombTemplates(int bombType) { 151 | if (!mBombTemplates.containsKey(bombType)) { 152 | GameData data = mBombData.get(bombType); 153 | 154 | Bitmap baseBitmap = BitmapManager.setAndGetBitmap(mContext, data.getDrawable()); 155 | List bitmaps = new ArrayList<>(); 156 | 157 | int baseWidth = baseBitmap.getWidth(); 158 | int baseHeight = baseBitmap.getHeight(); 159 | int width = baseWidth / 2; 160 | 161 | for (int x = 0; x < baseWidth; x += width) { 162 | Bitmap bitmap = Bitmap.createBitmap(baseBitmap, x, 0, width, baseHeight); 163 | bitmaps.add(bitmap); 164 | } 165 | 166 | mBombTemplates.put(bombType, bitmaps); 167 | } 168 | 169 | return mBombTemplates.get(bombType); 170 | } 171 | 172 | /** 173 | * 根据火焰类型和子类型(上、下、左、右、中)或者图片列表 174 | * 175 | * @param fireType 176 | * @param fireSubType 177 | * @return 178 | */ 179 | public List setAndGetFireTemplates(int fireType, int fireSubType) { 180 | if (!mFireTemplates.containsKey(fireType)) { 181 | GameData data = mFireData.get(fireType); 182 | Bitmap fireMain = BitmapManager.setAndGetBitmap(mContext, data.getDrawable()); 183 | 184 | int width = fireMain.getWidth() / 4; 185 | int height = fireMain.getHeight() / 7; 186 | 187 | Map> subFires = new HashMap<>(); 188 | for (int y = 0; y < 7; y++) { 189 | List list = new ArrayList<>(); 190 | for (int x = 0; x < 4; x++) { 191 | Bitmap bitmap = Bitmap.createBitmap(fireMain, x * width, y * height, width, height); 192 | list.add(bitmap); 193 | } 194 | 195 | subFires.put(y + 1, list); 196 | } 197 | 198 | mFireTemplates.put(fireType, subFires); 199 | } 200 | 201 | return mFireTemplates.get(fireType).get(fireSubType); 202 | } 203 | 204 | /** 205 | * 炸弹爆炸,创建火焰 206 | * 207 | * @param bomb 208 | */ 209 | public void explodBomb(Bomb bomb, int x, int y) { 210 | int fireLength = bomb.getFireLength(); 211 | 212 | BombFire middleFire = new BombFire(setAndGetFireTemplates(mFireType, BombFire.TYPE_CENTER)); 213 | middleFire.setMapPoint(x, y); 214 | mBombFires.add(middleFire); 215 | 216 | //火焰是否可以向四个方向延伸 217 | boolean spreadUp = true; 218 | boolean spreadDown = true; 219 | boolean spreadLeft = true; 220 | boolean spreadRight = true; 221 | 222 | int newX; 223 | int newY; 224 | 225 | for (int i = 1; i <= fireLength; i++) { 226 | //是否是最后一次循环 227 | boolean isLast = (i == fireLength); 228 | //上面 229 | newY = y - i; 230 | if (spreadUp && canSpread(mGameTiles[newY][x])) { 231 | BombFire upFire; 232 | if (!isLast) { 233 | upFire = new BombFire(setAndGetFireTemplates(mFireType, BombFire.TYPE_VERTICAL)); 234 | } else { 235 | upFire = new BombFire(setAndGetFireTemplates(mFireType, BombFire.TYPE_UP)); 236 | } 237 | upFire.setMapPoint(x, newY); 238 | mBombFires.add(upFire); 239 | } else { 240 | spreadUp = false; 241 | } 242 | 243 | //下面 244 | newY = y + i; 245 | if (spreadDown && canSpread(mGameTiles[newY][x])) { 246 | BombFire downFire; 247 | if (!isLast) { 248 | downFire = new BombFire(setAndGetFireTemplates(mFireType, BombFire.TYPE_VERTICAL)); 249 | } else { 250 | downFire = new BombFire(setAndGetFireTemplates(mFireType, BombFire.TYPE_DOWN)); 251 | } 252 | downFire.setMapPoint(x, newY); 253 | mBombFires.add(downFire); 254 | } else { 255 | spreadDown = false; 256 | } 257 | 258 | //左面 259 | newX = x - i; 260 | if (spreadLeft && canSpread(mGameTiles[y][newX])) { 261 | BombFire leftFire; 262 | if (!isLast) { 263 | leftFire = new BombFire(setAndGetFireTemplates(mFireType, BombFire.TYPE_HORIZONTAL)); 264 | } else { 265 | leftFire = new BombFire(setAndGetFireTemplates(mFireType, BombFire.TYPE_LEFT)); 266 | } 267 | leftFire.setMapPoint(newX, y); 268 | mBombFires.add(leftFire); 269 | } else { 270 | spreadLeft = false; 271 | } 272 | 273 | //右面 274 | newX = x + i; 275 | if (spreadRight && canSpread(mGameTiles[y][newX])) { 276 | BombFire rightFire; 277 | if (!isLast) { 278 | rightFire = new BombFire(setAndGetFireTemplates(mFireType, BombFire.TYPE_HORIZONTAL)); 279 | } else { 280 | rightFire = new BombFire(setAndGetFireTemplates(mFireType, BombFire.TYPE_RIGHT)); 281 | } 282 | rightFire.setMapPoint(newX, y); 283 | mBombFires.add(rightFire); 284 | } else { 285 | spreadRight = false; 286 | } 287 | 288 | } 289 | } 290 | 291 | /** 292 | * 放置炸弹 293 | * 294 | * @param mapPoint 295 | */ 296 | public void setBomb(Point mapPoint) { 297 | //地图上的相对点 298 | int mapX = mapPoint.x; 299 | int mapY = mapPoint.y; 300 | 301 | GameTile gameTile = mGameTiles[mapY][mapX]; 302 | if (gameTile != null && gameTile.isBlockerTile()) { 303 | Log.i(TAG, "setBomb: 炸弹已存在"); 304 | return; 305 | } 306 | 307 | addBomb(PlayerManager.mId, mBombType, mapX, mapY); 308 | //网络通告 309 | noticeSetBomb(PlayerManager.mId, mapX, mapY); 310 | } 311 | 312 | private void addBomb(int pid, int bombType, int mapX, int mapY) { 313 | //计算绝对坐标点 314 | Point point = new Point(); 315 | point.x = mapX * tileWidth; 316 | point.y = mapY * tileHeight; 317 | 318 | List bombTemplate = setAndGetBombTemplates(bombType); 319 | Bomb bomb = new Bomb(bombTemplate, true, point, pid); 320 | mGameTiles[mapY][mapX] = bomb; 321 | } 322 | 323 | private void noticeSetBomb(int pid, int mapX, int mapY) { 324 | if (!isMultiStage()) return; 325 | 326 | Message msg = Message.obtain(); 327 | msg.what = GameConstants.SET_BOMB; 328 | 329 | JSONObject jsonObject = new JSONObject(); 330 | try { 331 | jsonObject.put(ConnectConstants.TYPE, GameConstants.SET_BOMB); 332 | jsonObject.put(ConnectConstants.PLAYER_ID, pid); 333 | jsonObject.put(ConnectConstants.BOMB_TYPE, mBombType); 334 | jsonObject.put(ConnectConstants.MAP_X, mapX); 335 | jsonObject.put(ConnectConstants.MAP_Y, mapY); 336 | } catch (JSONException e) { 337 | e.printStackTrace(); 338 | } 339 | 340 | msg.obj = jsonObject; 341 | mHandler.sendMessage(msg); 342 | } 343 | 344 | public void handleCollision(Collision collision, Player player) { 345 | Point mapPoint = player.getStandardMapPoint(); 346 | int width = player.getWidth(); 347 | int height = player.getHeight(); 348 | 349 | mSurroundTiles.clear(); 350 | 351 | //首先获取周围8个位置和当前位置砖块,取当前位置是为了判断是否有火焰 352 | mSurroundTiles.add(mGameTiles[mapPoint.y - 1][mapPoint.x - 1]); //左上 353 | mSurroundTiles.add(mGameTiles[mapPoint.y - 1][mapPoint.x]); //上 354 | mSurroundTiles.add(mGameTiles[mapPoint.y - 1][mapPoint.x + 1]); //右上 355 | mSurroundTiles.add(mGameTiles[mapPoint.y][mapPoint.x - 1]); //左 356 | mSurroundTiles.add(mGameTiles[mapPoint.y][mapPoint.x]); //当前 357 | mSurroundTiles.add(mGameTiles[mapPoint.y][mapPoint.x + 1]); //右 358 | mSurroundTiles.add(mGameTiles[mapPoint.y + 1][mapPoint.x - 1]); //左下 359 | mSurroundTiles.add(mGameTiles[mapPoint.y + 1][mapPoint.x]); //下 360 | mSurroundTiles.add(mGameTiles[mapPoint.y + 1][mapPoint.x + 1]); //右下 361 | 362 | GameTile collisionTile = null; 363 | int newX = collision.getNewX(); 364 | int newY = collision.getNewY(); 365 | 366 | for (GameTile gameTile : mSurroundTiles) { 367 | if (gameTile != null && gameTile.isCollision(newX, newY, width, height)) { 368 | //如果存在冲突 369 | collisionTile = gameTile; 370 | break; 371 | } 372 | } 373 | if (collisionTile == null) { 374 | return; 375 | } 376 | 377 | if (collisionTile.getType() == GameTile.TYPE_BOMB) { 378 | Point nowPoint = player.getPoint(); 379 | if (collisionTile.isCollision(nowPoint.x, nowPoint.y, width, height)) { //如果炸弹和当前角色有交叉 380 | return; //允许通过 381 | } else { 382 | collision.setSolvable(false); //不许通过 383 | return; 384 | } 385 | } 386 | 387 | collision.setCollisionTile(collisionTile); 388 | collision.solveCollision(); 389 | } 390 | 391 | public List getBombFires() { 392 | return mBombFires; 393 | } 394 | 395 | /** 396 | * 判断砖块是否是空或者可以破环 397 | * 398 | * @param gameTile 399 | * @return 400 | */ 401 | private boolean canSpread(GameTile gameTile) { 402 | return gameTile == null || !gameTile.isBlockerTile(); 403 | } 404 | 405 | /** 406 | * 判断游戏是否为多人游戏 407 | * 408 | * @return 409 | */ 410 | public static boolean isMultiStage() { 411 | return gameStage == GameConstants.MULTI_PLAYER_STAGE; 412 | } 413 | 414 | /** 415 | * 处理网络炸弹放置 416 | */ 417 | public void handleSetBomb(JSONObject jsonObject) { 418 | int pid = 0; 419 | int bombType = 0; 420 | int mapX = 0; 421 | int mapY = 0; 422 | 423 | try { 424 | pid = jsonObject.getInt(ConnectConstants.PLAYER_ID); 425 | bombType = jsonObject.getInt(ConnectConstants.BOMB_TYPE); 426 | mapX = jsonObject.getInt(ConnectConstants.MAP_X); 427 | mapY = jsonObject.getInt(ConnectConstants.MAP_Y); 428 | } catch (JSONException e) { 429 | e.printStackTrace(); 430 | } 431 | 432 | addBomb(pid, bombType, mapX, mapY); 433 | } 434 | } 435 | -------------------------------------------------------------------------------- /app/src/main/java/com/kuoruan/bomberman/view/GameView.java: -------------------------------------------------------------------------------- 1 | package com.kuoruan.bomberman.view; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.Canvas; 7 | import android.graphics.Paint; 8 | import android.graphics.Point; 9 | import android.util.DisplayMetrics; 10 | import android.util.Log; 11 | import android.view.Display; 12 | import android.view.MotionEvent; 13 | import android.view.SurfaceHolder; 14 | import android.view.SurfaceView; 15 | 16 | import com.kuoruan.bomberman.R; 17 | import com.kuoruan.bomberman.entity.Bomb; 18 | import com.kuoruan.bomberman.entity.BombFire; 19 | import com.kuoruan.bomberman.entity.Collision; 20 | import com.kuoruan.bomberman.entity.GameTile; 21 | import com.kuoruan.bomberman.entity.GameUi; 22 | import com.kuoruan.bomberman.entity.Player; 23 | import com.kuoruan.bomberman.util.BitmapManager; 24 | import com.kuoruan.bomberman.util.SceneManager; 25 | import com.kuoruan.bomberman.util.PlayerManager; 26 | 27 | import java.util.ArrayList; 28 | import java.util.Collection; 29 | import java.util.List; 30 | import java.util.Map; 31 | 32 | /** 33 | * 游戏地图view 34 | */ 35 | public class GameView extends SurfaceView implements SurfaceHolder.Callback { 36 | 37 | private static final String TAG = "GameView"; 38 | //每30毫秒刷新一次屏幕 39 | public static final int TIME_IN_FRAME = 30; 40 | //控制按钮边距 41 | private static final int CONTROLS_PADDING = 10; 42 | //游戏状态 43 | public static final int STATE_RUNNING = 1; 44 | public static final int STATE_PAUSED = 2; 45 | 46 | //屏幕大小 47 | private int mScreenXMax = 0; 48 | private int mScreenYMax = 0; 49 | private int mScreenXCenter = 0; 50 | private int mScreenYCenter = 0; 51 | 52 | /** 53 | * 屏幕偏移 54 | */ 55 | private int mScreenXOffset = 0; 56 | private int mScreenYOffset = 0; 57 | 58 | //主线程运行 59 | private boolean mGameRun = true; 60 | //游戏状态 61 | private int mGameState; 62 | 63 | private Display mDisplay; 64 | 65 | //屏幕像素密度 66 | private float mScreenDensity = 0.0f; 67 | private SurfaceHolder mGameSurfaceHolder; 68 | //正在处理地图 69 | private boolean updatingGameTiles = false; 70 | 71 | //当前玩家 72 | private Player mPlayer; 73 | 74 | //文字画笔 75 | private Paint mUiTextPaint; 76 | private Context mGameContext; 77 | 78 | //控制按钮 79 | private GameUi mCtrlUpArrow; 80 | private GameUi mCtrlDownArrow; 81 | private GameUi mCtrlLeftArrow; 82 | private GameUi mCtrlRightArrow; 83 | private GameUi mSetBombButton; 84 | //背景图片 85 | private Bitmap mBackgroundImage; 86 | 87 | //地图对象数组 88 | private GameTile[][] mGameTiles; 89 | private List mBombFires; 90 | private List mEndFires = new ArrayList<>(); 91 | 92 | private PlayerManager mPlayerManager; 93 | private SceneManager mSceneManager; 94 | private Map mPlayers; 95 | //冲突对象 96 | private Collision mCollision = new Collision(); 97 | 98 | //游戏主线程 99 | private GameThread mThread; 100 | 101 | public GameView(Context context) { 102 | super(context); 103 | } 104 | 105 | public GameView(Context context, PlayerManager playerManager, SceneManager sceneManager, Display display) { 106 | super(context); 107 | mGameContext = context; 108 | mDisplay = display; 109 | 110 | mPlayerManager = playerManager; 111 | mPlayer = mPlayerManager.getMyPlayer(); 112 | mPlayers = mPlayerManager.getPlayers(); 113 | 114 | mSceneManager = sceneManager; 115 | mBombFires = mSceneManager.getBombFires(); 116 | 117 | SurfaceHolder holder = getHolder(); 118 | holder.addCallback(this); 119 | //游戏主线程 120 | mThread = new GameThread(holder, context); 121 | 122 | setFocusable(true); 123 | 124 | //初始化关卡数据 125 | startLevel(); 126 | mThread.doStart(); 127 | } 128 | 129 | @Override 130 | public void surfaceCreated(SurfaceHolder holder) { 131 | if (mThread.getState() == Thread.State.TERMINATED) { 132 | mThread = new GameThread(holder, getContext()); 133 | mThread.setRunning(true); 134 | mThread.start(); 135 | mThread.doStart(); 136 | startLevel(); 137 | } else { 138 | mThread.setRunning(true); 139 | mThread.start(); 140 | } 141 | } 142 | 143 | @Override 144 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 145 | mThread.setSurfaceSize(width, height); 146 | } 147 | 148 | @Override 149 | public void surfaceDestroyed(SurfaceHolder holder) { 150 | boolean retry = true; 151 | mThread.setRunning(false); 152 | while (retry) { 153 | try { 154 | mThread.join(); 155 | retry = false; 156 | } catch (InterruptedException e) { 157 | Log.e(TAG, e.getMessage()); 158 | } 159 | } 160 | } 161 | 162 | public class GameThread extends Thread { 163 | 164 | //画布 165 | private Canvas canvas = null; 166 | 167 | public GameThread(SurfaceHolder surfaceHolder, Context context) { 168 | mGameSurfaceHolder = surfaceHolder; 169 | mGameContext = context; 170 | mBackgroundImage = BitmapFactory.decodeResource(context.getResources(), R.drawable.game_bg); 171 | 172 | //获取屏幕相关数据 173 | Point point = new Point(); 174 | mDisplay.getSize(point); 175 | mScreenXMax = point.x; 176 | mScreenYMax = point.y; 177 | 178 | mScreenXCenter = (mScreenXMax / 2); 179 | mScreenYCenter = (mScreenYMax / 2); 180 | //获取屏幕像素密度 181 | DisplayMetrics outMetrics = new DisplayMetrics(); 182 | mDisplay.getMetrics(outMetrics); 183 | mScreenDensity = outMetrics.density; 184 | 185 | setGameStartState(); 186 | } 187 | 188 | @Override 189 | public void run() { 190 | while (mGameRun) { 191 | //取得更新游戏之前的时间 192 | long startTime = System.currentTimeMillis(); 193 | try { 194 | synchronized (mGameSurfaceHolder) { 195 | canvas = mGameSurfaceHolder.lockCanvas(); 196 | if (mGameState == STATE_RUNNING) { 197 | updatePlayer(); 198 | } 199 | doDraw(); 200 | } 201 | } finally { 202 | if (canvas != null) { 203 | mGameSurfaceHolder.unlockCanvasAndPost(canvas); 204 | } 205 | } 206 | 207 | long endTime = System.currentTimeMillis(); 208 | //计算出游戏一次更新的毫秒数 209 | int diffTime = (int) (endTime - startTime); 210 | 211 | //确保每次更新时间为30毫秒 212 | while (diffTime <= TIME_IN_FRAME) { 213 | diffTime = (int) (System.currentTimeMillis() - startTime); 214 | //线程等待 215 | Thread.yield(); 216 | } 217 | 218 | } 219 | } 220 | 221 | public void setSurfaceSize(int width, int height) { 222 | synchronized (mGameSurfaceHolder) { 223 | mBackgroundImage = Bitmap.createScaledBitmap(mBackgroundImage, width, height, true); 224 | } 225 | } 226 | 227 | 228 | public void setRunning(boolean run) { 229 | mGameRun = run; 230 | } 231 | 232 | //更新自身游戏角色位置 233 | private void updatePlayer() { 234 | if (mPlayer != null && mPlayer.isMoving()) { 235 | int newX = mPlayer.getX(); 236 | int newY = mPlayer.getY(); 237 | int direction = mPlayer.getDirection(); 238 | int pixelValue = getPixelValueForDensity(mPlayer.getSpeed()); 239 | 240 | switch (direction) { 241 | case Player.DIRECTION_UP: 242 | newY -= pixelValue; 243 | break; 244 | case Player.DIRECTION_DOWN: 245 | newY += pixelValue; 246 | break; 247 | case Player.DIRECTION_LEFT: 248 | newX -= pixelValue; 249 | break; 250 | case Player.DIRECTION_RIGHT: 251 | newX += pixelValue; 252 | break; 253 | } 254 | 255 | mCollision.setDirection(direction); 256 | mCollision.setSolvable(true); 257 | mCollision.setNewX(newX); 258 | mCollision.setNewY(newY); 259 | 260 | mSceneManager.handleCollision(mCollision, mPlayer); 261 | 262 | if (mCollision.isSolvable()) { 263 | mPlayer.setX(mCollision.getNewX()); 264 | mPlayer.setY(mCollision.getNewY()); 265 | mPlayerManager.noticeMyMove(); 266 | setViewOffset(); 267 | } else { 268 | handleTileCollision(mCollision.getCollisionTile()); 269 | } 270 | 271 | } 272 | } 273 | 274 | //绘制游戏元素 275 | private void doDraw() { 276 | if (canvas != null) { 277 | canvas.drawBitmap(mBackgroundImage, 0, 0, null); 278 | 279 | if (!updatingGameTiles) { 280 | drawGameTiles(); 281 | } 282 | drawBombFires(); 283 | drawPlayers(); 284 | drawControls(); 285 | 286 | //canvas.drawText(mLastStatusMessage, 30, 50, mUiTextPaint); 287 | } 288 | } 289 | 290 | //绘制玩家 291 | private void drawPlayers() { 292 | int offsetX; 293 | int offsetY; 294 | 295 | Collection players = mPlayers.values(); 296 | for (Player player : players) { 297 | offsetX = player.getX() - mScreenXOffset; 298 | offsetY = player.getY() - mScreenYOffset; 299 | //Log.i(TAG, "drawPlayers: player" + player.getId() + " is moving " + player.isMoving()); 300 | if (player.isMoving() || (!player.isAlive() && !player.isEnd())) { 301 | player.doAnimation(); 302 | } 303 | canvas.drawBitmap(player.getBitmap(), offsetX, offsetY, null); 304 | } 305 | } 306 | 307 | //绘制游戏地图 308 | private void drawGameTiles() { 309 | int offsetX; 310 | int offsetY; 311 | int type; 312 | GameTile gameTile; 313 | 314 | for (int i = 0; i < mGameTiles.length; i++) { 315 | for (int j = 0; j < mGameTiles[i].length; j++) { 316 | gameTile = mGameTiles[i][j]; 317 | if (gameTile == null) { 318 | continue; 319 | } 320 | offsetX = gameTile.getX() - mScreenXOffset; 321 | offsetY = gameTile.getY() - mScreenYOffset; 322 | 323 | if (gameTile.isVisible()) { 324 | type = gameTile.getType(); //获取砖块类型 325 | 326 | switch (type) { 327 | case GameTile.TYPE_BOMB: 328 | Bomb bomb = (Bomb) gameTile; 329 | if (bomb.isExplosion()) { //如果炸弹已爆炸 330 | mSceneManager.explodBomb(bomb, j, i); //引爆炸弹 331 | mGameTiles[i][j] = null; 332 | } else { 333 | bomb.doAnimation(); 334 | } 335 | break; 336 | 337 | default: 338 | break; 339 | } 340 | 341 | canvas.drawBitmap(gameTile.getBitmap(), offsetX, offsetY, null); 342 | } 343 | } 344 | } 345 | } 346 | 347 | private void drawBombFires() { 348 | if (mBombFires.size() == 0) { 349 | return; 350 | } 351 | 352 | int offsetX; 353 | int offsetY; 354 | for (GameTile fire : mBombFires) { 355 | if (fire.isCollision(mPlayer.getX(), mPlayer.getY(), mPlayer.getWidth(), mPlayer.getHeight())) { 356 | mPlayerManager.noticeMyDie(); 357 | } 358 | if (fire.isEnd()) { 359 | mEndFires.add(fire); 360 | } else { 361 | offsetX = fire.getX() - mScreenXOffset; 362 | offsetY = fire.getY() - mScreenYOffset; 363 | fire.doAnimation(); 364 | canvas.drawBitmap(fire.getBitmap(), offsetX, offsetY, null); 365 | } 366 | } 367 | 368 | mBombFires.removeAll(mEndFires); 369 | mEndFires.clear(); 370 | } 371 | 372 | //绘制控制按钮 373 | private void drawControls() { 374 | canvas.drawBitmap(mCtrlUpArrow.getBitmap(), mCtrlUpArrow.getX(), mCtrlUpArrow.getY(), null); 375 | canvas.drawBitmap(mCtrlDownArrow.getBitmap(), mCtrlDownArrow.getX(), mCtrlDownArrow.getY(), null); 376 | canvas.drawBitmap(mCtrlLeftArrow.getBitmap(), mCtrlLeftArrow.getX(), mCtrlLeftArrow.getY(), null); 377 | canvas.drawBitmap(mCtrlRightArrow.getBitmap(), mCtrlRightArrow.getX(), mCtrlRightArrow.getY(), null); 378 | canvas.drawBitmap(mSetBombButton.getBitmap(), mSetBombButton.getX(), mSetBombButton.getY(), null); 379 | } 380 | 381 | //设置游戏状态 382 | public void setState(int state) { 383 | mGameState = state; 384 | } 385 | 386 | //游戏暂停 387 | public void pause() { 388 | synchronized (mGameSurfaceHolder) { 389 | if (mGameState == STATE_RUNNING) { 390 | setState(STATE_PAUSED); 391 | } 392 | } 393 | } 394 | 395 | //开始游戏 396 | public void doStart() { 397 | setState(STATE_RUNNING); 398 | } 399 | 400 | //游戏继续 401 | public void unPause() { 402 | synchronized (mGameSurfaceHolder) { 403 | if (mGameState != STATE_RUNNING) { 404 | setState(STATE_RUNNING); 405 | } 406 | } 407 | } 408 | 409 | private void setGameStartState() { 410 | setControlsStart(); 411 | setViewOffset(); 412 | } 413 | 414 | //处理控制按钮 415 | private void setControlsStart() { 416 | if (mCtrlDownArrow == null) { 417 | 418 | mCtrlDownArrow = new GameUi(BitmapManager.setAndGetBitmap(mGameContext, R.drawable.ctrl_down_arrow)); 419 | 420 | mCtrlDownArrow.setX((mCtrlDownArrow.getWidth() + getPixelValueForDensity(CONTROLS_PADDING))); 421 | mCtrlDownArrow.setY(mScreenYMax - (mCtrlDownArrow.getHeight() + getPixelValueForDensity 422 | (CONTROLS_PADDING))); 423 | } 424 | 425 | if (mCtrlUpArrow == null) { 426 | mCtrlUpArrow = new GameUi(BitmapManager.setAndGetBitmap(mGameContext, R.drawable.ctrl_up_arrow)); 427 | 428 | mCtrlUpArrow.setX(mCtrlDownArrow.getX()); 429 | mCtrlUpArrow.setY(mCtrlDownArrow.getY() - (mCtrlUpArrow.getHeight() * 2)); 430 | } 431 | 432 | if (mCtrlLeftArrow == null) { 433 | mCtrlLeftArrow = new GameUi(BitmapManager.setAndGetBitmap(mGameContext, R.drawable.ctrl_left_arrow)); 434 | mCtrlLeftArrow.setX(mCtrlDownArrow.getX() - mCtrlLeftArrow.getWidth()); 435 | mCtrlLeftArrow.setY(mCtrlDownArrow.getY() - mCtrlLeftArrow.getHeight()); 436 | } 437 | 438 | if (mCtrlRightArrow == null) { 439 | mCtrlRightArrow = new GameUi(BitmapManager.setAndGetBitmap(mGameContext, R.drawable.ctrl_right_arrow)); 440 | 441 | mCtrlRightArrow.setX(mCtrlLeftArrow.getX() + (mCtrlRightArrow.getWidth() * 2)); 442 | mCtrlRightArrow.setY(mCtrlLeftArrow.getY()); 443 | } 444 | 445 | if (mSetBombButton == null) { 446 | mSetBombButton = new GameUi(BitmapManager.setAndGetBitmap(mGameContext, R.drawable.set_bomb)); 447 | 448 | mSetBombButton.setX(mScreenXMax - (mSetBombButton.getWidth() + getPixelValueForDensity 449 | (CONTROLS_PADDING))); 450 | mSetBombButton.setY(mScreenYMax - (mSetBombButton.getHeight() * 2 + getPixelValueForDensity 451 | (CONTROLS_PADDING))); 452 | } 453 | } 454 | 455 | } 456 | 457 | public GameThread getGameThread() { 458 | return mThread; 459 | } 460 | 461 | private void startLevel() { 462 | parseGameLevelData(); 463 | setViewOffset(); 464 | 465 | mThread.unPause(); 466 | } 467 | 468 | //处理游戏数据 469 | private void parseGameLevelData() { 470 | updatingGameTiles = true; 471 | 472 | mGameTiles = mSceneManager.parseGameTileData(); 473 | 474 | if (mGameTiles == null) { 475 | return; 476 | } 477 | 478 | //处理地图数据 479 | updatingGameTiles = false; 480 | } 481 | 482 | /** 483 | * 计算屏幕偏移 484 | */ 485 | private void setViewOffset() { 486 | if (SceneManager.sceneXMax == 0 || SceneManager.sceneYMax == 0) { 487 | return; 488 | } 489 | 490 | int playerX = mPlayer.getX(); 491 | int playerY = mPlayer.getY(); 492 | 493 | if (playerX >= mScreenXCenter) { 494 | mScreenXOffset = playerX - mScreenXCenter; 495 | 496 | if (mScreenXOffset > (SceneManager.sceneXMax - mScreenXMax)) { 497 | mScreenXOffset = SceneManager.sceneXMax - mScreenXMax; 498 | } 499 | } 500 | 501 | if (playerY >= mScreenYCenter) { 502 | mScreenYOffset = playerY - mScreenYCenter; 503 | 504 | if (mScreenYOffset > (SceneManager.sceneYMax - mScreenYMax)) { 505 | mScreenYOffset = SceneManager.sceneYMax - mScreenYMax; 506 | } 507 | } 508 | 509 | } 510 | 511 | //处理点击触摸事件 512 | @Override 513 | public boolean onTouchEvent(MotionEvent event) { 514 | 515 | if (!mPlayer.isAlive()) { 516 | Log.i(TAG, "onTouchEvent: 角色已死亡"); 517 | return false; 518 | } 519 | 520 | int x = (int) event.getX(0); 521 | int y = (int) event.getY(0); 522 | switch (event.getAction() & MotionEvent.ACTION_MASK) { 523 | case MotionEvent.ACTION_POINTER_DOWN: 524 | //获取第二个按下点的坐标 525 | x = (int) event.getX(1); 526 | y = (int) event.getY(1); 527 | case MotionEvent.ACTION_DOWN: 528 | 529 | if (mGameState == STATE_RUNNING) { 530 | if (mSetBombButton.getImpact(x, y)) { 531 | mSceneManager.setBomb(mPlayer.getStandardMapPoint()); 532 | } else if (mCtrlUpArrow.getImpact(x, y)) { 533 | mPlayer.setDirection(Player.DIRECTION_UP); 534 | mPlayer.setState(Player.STATE_MOVING); 535 | } else if (mCtrlDownArrow.getImpact(x, y)) { 536 | mPlayer.setDirection(Player.DIRECTION_DOWN); 537 | mPlayer.setState(Player.STATE_MOVING); 538 | } else if (mCtrlLeftArrow.getImpact(x, y)) { 539 | mPlayer.setDirection(Player.DIRECTION_LEFT); 540 | mPlayer.setState(Player.STATE_MOVING); 541 | } else if (mCtrlRightArrow.getImpact(x, y)) { 542 | mPlayer.setDirection(Player.DIRECTION_RIGHT); 543 | mPlayer.setState(Player.STATE_MOVING); 544 | } 545 | } 546 | break; 547 | case MotionEvent.ACTION_UP: 548 | case MotionEvent.ACTION_CANCEL: 549 | mPlayer.setState(Player.STATE_STOP); 550 | mPlayer.setDirection(0); 551 | mPlayerManager.noticeMyStop(); 552 | break; 553 | } 554 | 555 | return true; 556 | } 557 | 558 | //处理冲突 559 | private void handleTileCollision(GameTile gameTile) { 560 | if (gameTile != null) { 561 | switch (gameTile.getType()) { 562 | case GameTile.TYPE_OBSTACLE: 563 | Log.i(TAG, "handleTileCollision: 外岩"); 564 | break; 565 | case GameTile.TYPE_ROCK: 566 | Log.i(TAG, "handleTileCollision: 普通岩石"); 567 | break; 568 | case GameTile.TYPE_CRATES: 569 | Log.i(TAG, "handleTileCollision: 木箱子"); 570 | break; 571 | case GameTile.TYPE_BOMB: 572 | Log.i(TAG, "handleTileCollision: 炸弹"); 573 | break; 574 | case GameTile.TYPE_BOMB_FIRE: 575 | Log.i(TAG, "handleTileCollision: 火焰"); 576 | mPlayerManager.noticeMyDie(); //处理角色死亡 577 | break; 578 | default: 579 | Log.i(TAG, "handleTileCollision: 其他冲突"); 580 | } 581 | } 582 | } 583 | 584 | private int getPixelValueForDensity(int pixels) { 585 | return (int) (pixels * mScreenDensity); 586 | } 587 | } 588 | 589 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/background.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/bomb_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/bomb_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/bomb_1_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/bomb_1_0.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/bomb_1_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/bomb_1_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/btn_circle_style.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/btn_white_style.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ctrl_down_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/ctrl_down_arrow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ctrl_left_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/ctrl_left_arrow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ctrl_right_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/ctrl_right_arrow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ctrl_up_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/ctrl_up_arrow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/fire.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/game_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/game_bg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/player_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/player_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/player_unit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/player_unit.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/set_bomb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/set_bomb.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/tile_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/tile_01.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/tile_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/tile_02.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/tile_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-hdpi/tile_03.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/bomb_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-mdpi/bomb_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/bomb_1_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-mdpi/bomb_1_0.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/bomb_1_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-mdpi/bomb_1_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ctrl_down_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-mdpi/ctrl_down_arrow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ctrl_left_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-mdpi/ctrl_left_arrow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ctrl_right_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-mdpi/ctrl_right_arrow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ctrl_up_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-mdpi/ctrl_up_arrow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-mdpi/fire.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/game_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-mdpi/game_bg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/player_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-mdpi/player_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/player_unit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-mdpi/player_unit.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/set_bomb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-mdpi/set_bomb.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/tile_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-mdpi/tile_01.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/tile_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-mdpi/tile_02.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/tile_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kuoruan/Android-BomberMan/c98d5f6eef3c9393fa8adc119947f1930986aba9/app/src/main/res/drawable-mdpi/tile_03.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_connection.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 |