服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Android - Android植物大战僵尸小游戏

Android植物大战僵尸小游戏

2021-04-21 17:50蔡都平 Android

植物大战僵尸小游戏,无论老少皆爱,非常有意思,具有挑战性,那么基于代码是怎么实现的呢?下面通过本文给大家介绍Android植物大战僵尸小游戏,感兴趣的朋友一起学习吧

android植物大战僵尸小游戏全部内容如下:

Android植物大战僵尸小游戏

具体代码如下所示:

?
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
package com.example.liu.mygame;
import com.example.liu.mygame.global.config;
import com.example.liu.mygame.tools.devicetools;
import com.example.liu.mygame.view.gameview;
import android.os.bundle;
import android.app.activity;
import android.graphics.bitmapfactory;
import android.view.menu;
import android.view.motionevent;
public class mainactivity extends activity {
 private gameview gameview;
 @override
 public void oncreate(bundle savedinstancestate) {
  super.oncreate(savedinstancestate);
  init();
  gameview = new gameview(this);
  setcontentview(gameview);
 }
 // 初始化游戏资源
 private void init() {
  // todo auto-generated method stub
  // 获取屏幕大小尺寸
  config.devicewidth = devicetools.getdeviceinfo(this)[0];
  config.deviceheight = devicetools.getdeviceinfo(this)[1];
  // 得到原始图片
  config.gamebk = bitmapfactory.decoderesource(getresources(),
    r.drawable.bk);
  // 获取缩放比
  config.scalewidth = config.devicewidth
    / (float) config.gamebk.getwidth();
  config.scaleheight = config.deviceheight
    / (float) config.gamebk.getheight();
  // 处理图片让它成为目标图片
  config.gamebk = devicetools.resizebitmap(config.gamebk);
  config.seedbank = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.seedbank));
  // 绘制出卡片,不能进行等比缩放要进行目标大小的输入控制
  config.seedflower = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.seed_flower),
    config.seedbank.getwidth() / 10,
    config.seedbank.getheight() * 8 / 10);
  config.seedpea = devicetools.resizebitmap(bitmapfactory.decoderesource(
    getresources(), r.drawable.seed_pea), config.seedbank
    .getwidth() / 10, config.seedbank.getheight() * 8 / 10);
  // 初始化阳光图片
  config.sun = devicetools.resizebitmap(bitmapfactory.decoderesource(
    getresources(), r.drawable.sun));
  // 初始化子弹图片
  config.bullet = devicetools.resizebitmap(bitmapfactory.decoderesource(
    getresources(), r.drawable.bullet));
  // 初始化gameover图片
  config.gameover = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.gameover));
  // 初始化动态图片帧
  config.flowerframes[0] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_1_01));
  config.flowerframes[1] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_1_02));
  config.flowerframes[2] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_1_03));
  config.flowerframes[3] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_1_04));
  config.flowerframes[4] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_1_05));
  config.flowerframes[5] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_1_06));
  config.flowerframes[6] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_1_07));
  config.flowerframes[7] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_1_08));
  config.peaframes[0] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_2_01));
  config.peaframes[1] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_2_02));
  config.peaframes[2] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_2_03));
  config.peaframes[3] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_2_04));
  config.peaframes[4] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_2_05));
  config.peaframes[5] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_2_06));
  config.peaframes[6] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_2_07));
  config.peaframes[7] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.p_2_08));
  config.zombieframes[0] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.z_1_01));
  config.zombieframes[1] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.z_1_02));
  config.zombieframes[2] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.z_1_03));
  config.zombieframes[3] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.z_1_04));
  config.zombieframes[4] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.z_1_05));
  config.zombieframes[5] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.z_1_06));
  config.zombieframes[6] = devicetools.resizebitmap(bitmapfactory
    .decoderesource(getresources(), r.drawable.z_1_07));
 }
 // 重写ontouch触摸响应事件,返回值为gameview中生成的ontouch事件值
 @override
 public boolean ontouchevent(motionevent event) {
  // todo auto-generated method stub
  return gameview.ontouchevent(event);
 }
 // 销毁
 @override
 protected void ondestroy() {
  super.ondestroy();
 }
 // 停止
 @override
 protected void onpause() {
  super.onpause();
 }
 // 重启
 @override
 protected void onresume() {
  super.onresume();
 }
}
package com.example.liu.mygame.entity;
import android.graphics.canvas;
import android.graphics.paint;
import com.example.liu.mygame.global.config;
import com.example.liu.mygame.model.basemodel;
public class bullet extends basemodel {
 // 位置
 private int locationx;
 private int locationy;
 // 生命
 private boolean isalife;
 // 子弹产生时间
 private long birthtime = 0l;
 // x方向上的速度分量
 // 根据帧数,确定移动时间,然后来确定移动方式
 private float speedx = 10;
 public bullet(int locationx, int locationy) {
  this.locationx = locationx + 40;
  this.locationy = locationy + 20;
  this.isalife = true;
  // 获取系统时间
  birthtime = system.currenttimemillis();
 }
 @override
 public void drawself(canvas canvas, paint paint) {
  // todo auto-generated method stub
  if (isalife) {
   // 移动
   locationx += speedx;
   // 如果图片的y轴坐标移动到超出屏幕或者说移动到与屏幕齐平,那么生命周期结束
   if (locationx > config.devicewidth) {
    // 去除子弹
    isalife = false;
   }
  }
  canvas.drawbitmap(config.bullet, locationx, locationy, paint);
 }
 @override
 public int getmodelwidth() {
  // todo auto-generated method stub
  return config.bullet.getwidth();
 }
 public int getlocationx() {
  return locationx;
 }
 public void setlocationx(int locationx) {
  this.locationx = locationx;
 }
 public int getlocationy() {
  return locationy;
 }
 public void setlocationy(int locationy) {
  this.locationy = locationy;
 }
 public boolean isalife() {
  return isalife;
 }
 public void setalife(boolean isalife) {
  this.isalife = isalife;
 }
}
package com.example.liu.mygame.entity;
import android.graphics.canvas;
import android.graphics.paint;
import android.graphics.rect;
import android.view.motionevent;
import com.example.liu.mygame.global.config;
import com.example.liu.mygame.model.basemodel;
import com.example.liu.mygame.model.touchable;
import com.example.liu.mygame.view.gameview;
public class emplaceflower extends basemodel implements touchable {
 private int locationx;
 private int locationy;
 // 生命
 private boolean isalife;
 // 触摸区域(矩形)
 private rect toucharea;
 public emplaceflower(int locationx, int locationy) {
  this.locationx = locationx;
  this.locationy = locationy;
  this.isalife = true;
  // 初始化触摸响应矩形区域,与整体屏幕一致大小
  toucharea = new rect(0, 0, config.devicewidth, config.deviceheight);
 }
 @override
 public void drawself(canvas canvas, paint paint) {
  // todo auto-generated method stub
  if (isalife) {
   canvas.drawbitmap(config.flowerframes[0], locationx, locationy,
     paint);
  }
 }
 @override
 public boolean ontouch(motionevent event) {
  // todo auto-generated method stub
  int x = (int) event.getx();
  int y = (int) event.gety();
  // 如果点击的地方是在矩形区域内,那么开始设置跟随
  if (toucharea.contains(x, y)) {
   // 图标跟随
   // switch中需要相应三个事件:按下、抬起、拖动
   switch (event.getaction()) {
   case motionevent.action_down:
    break;
   case motionevent.action_move:
    // drawself方法已定,那么我们需要改变表示位置的两个变量,同时也要改变响应点击的区域toucharea
    locationx = x - config.flowerframes[0].getwidth() / 2;
    locationy = y - config.flowerframes[0].getheight() / 2;
    break;
   case motionevent.action_up:
    // 放手以后此移动中的实例的生命周期结束并在特定点产生新的固定的实例
    isalife = false;
    // 交由gameview处理
    gameview.getinstance().applay4plant(locationx, locationy, this);
    break;
   }
  }
  return false;
 }
 public int getlocationx() {
  return locationx;
 }
 public void setlocationx(int locationx) {
  this.locationx = locationx;
 }
 public int getlocationy() {
  return locationy;
 }
 public void setlocationy(int locationy) {
  this.locationy = locationy;
 }
 public boolean isalife() {
  return isalife;
 }
 public void setalife(boolean isalife) {
  this.isalife = isalife;
 }
}
package com.example.liu.mygame.entity;
import android.graphics.canvas;
import android.graphics.paint;
import android.graphics.rect;
import android.view.motionevent;
import com.example.liu.mygame.global.config;
import com.example.liu.mygame.model.basemodel;
import com.example.liu.mygame.model.touchable;
import com.example.liu.mygame.view.gameview;
public class emplacepea extends basemodel implements touchable {
 private int locationx;
 private int locationy;
 // 生命
 private boolean isalife;
 // 触摸区域(矩形)
 private rect toucharea;
 public emplacepea(int locationx, int locationy) {
  this.locationx = locationx;
  this.locationy = locationy;
  this.isalife = true;
  // 初始化触摸响应矩形区域
  toucharea = new rect(0, 0, config.devicewidth, config.deviceheight);
 }
 @override
 public void drawself(canvas canvas, paint paint) {
  // todo auto-generated method stub
  if (isalife) {
   canvas.drawbitmap(config.peaframes[0], locationx, locationy, paint);
  }
 }
 @override
 public boolean ontouch(motionevent event) {
  // todo auto-generated method stub
  int x = (int) event.getx();
  int y = (int) event.gety();
  // 如果点击的地方是在矩形区域内,那么开始设置跟随
  if (toucharea.contains(x, y)) {
   // 图标跟随
   // switch中需要相应三个事件:按下、抬起、拖动
   switch (event.getaction()) {
   case motionevent.action_down:
    break;
   case motionevent.action_move:
    // drawself方法已定,那么我们需要改变表示位置的两个变量,同时也要改变响应点击的区域toucharea
    locationx = x - config.peaframes[0].getwidth() / 2;
    locationy = y - config.peaframes[0].getheight() / 2;
    break;
   case motionevent.action_up:
    // 放手以后此移动中的实例的生命周期结束并在特定点产生新的固定的实例
    isalife = false;
    // 交由gameview处理
    gameview.getinstance().applay4plant(locationx, locationy, this);
    break;
   }
  }
  return false;
 }
 public int getlocationx() {
  return locationx;
 }
 public void setlocationx(int locationx) {
  this.locationx = locationx;
 }
 public int getlocationy() {
  return locationy;
 }
 public void setlocationy(int locationy) {
  this.locationy = locationy;
 }
 public boolean isalife() {
  return isalife;
 }
 public void setalife(boolean isalife) {
  this.isalife = isalife;
 }
}
package com.example.liu.mygame.entity;
import android.graphics.canvas;
import android.graphics.paint;
import com.example.liu.mygame.global.config;
import com.example.liu.mygame.model.basemodel;
import com.example.liu.mygame.model.plant;
import com.example.liu.mygame.view.gameview;
//豌豆射手实体类
public class flower extends basemodel implements plant {
 private int locationx;
 private int locationy;
 private boolean isalife;
 // 图片帧数组的下标
 private int frameindex = 0;
 // 一个标记通过此标记确定此处是否有植物
 private int mapindex;
 // 控制产生阳光的时间
 private long lastbirthtime;
 // 摆动速度控制,两帧一动
 private boolean swingspeed;
 public flower(int locationx, int locationy, int mapindex) {
  this.locationx = locationx;
  this.locationy = locationy;
  this.mapindex = mapindex;
  isalife = true;
  // 初始化时间用来确保初试时间与花的创造时间一致
  lastbirthtime = system.currenttimemillis();
  swingspeed = false;
 }
 @override
 public void drawself(canvas canvas, paint paint) {
  // todo auto-generated method stub
  if (isalife) {
   // 这里绘入的bitmap就需要在绘制自己的时候换自己的帧动画以形成动态效果
   // 这个组在config中已经初始化好了
   canvas.drawbitmap(config.flowerframes[frameindex], locationx,
     locationy, paint);
   // 用此变量让数组变化
   // 通过这样的取模方法,可以让这个frameindex值不超过7
   // 当frameindex为8时会变为0,避免数组越界
   if (!swingspeed) {
    frameindex = (++frameindex) % 8;
    swingspeed = false;
   } else {
    swingspeed = true;
   }
   // 用此处判断来确定每10秒一个阳光的产生
   if (system.currenttimemillis() - lastbirthtime > 10000) {
    lastbirthtime = system.currenttimemillis();
    givebirth2sun();
   }
  }
 }
 // 产生阳光
 // 阳光具有生命,然后两种情况,被点击则转换状态,移动到上方阳光的标志处,过一段时间不点击则死亡消失
 // 产生在花的位置上
 private void givebirth2sun() {
  // 首先要有阳光的图层集合,处于第三层,那么就需要操作集合,就需要调用gameview.getinstance
  gameview.getinstance().givebrith2sun(locationx, locationy);
 }
 @override
 public int getmodelwidth() {
  // todo auto-generated method stub
  return config.flowerframes[0].getwidth();
 }
 public int getlocationx() {
  return locationx;
 }
 public void setlocationx(int locationx) {
  this.locationx = locationx;
 }
 public int getlocationy() {
  return locationy;
 }
 public void setlocationy(int locationy) {
  this.locationy = locationy;
 }
 public boolean isalife() {
  return isalife;
 }
 public void setalife(boolean isalife) {
  this.isalife = isalife;
 }
 @override
 public int getmapindex() {
  // todo auto-generated method stub
  return mapindex;
 }
}
package com.example.liu.mygame.entity;
import android.graphics.canvas;
import android.graphics.paint;
import android.util.log;
import com.example.liu.mygame.global.config;
import com.example.liu.mygame.model.basemodel;
import com.example.liu.mygame.model.plant;
import com.example.liu.mygame.view.gameview;
//豌豆射手实体类
public class pea extends basemodel implements plant {
 private int locationx;
 private int locationy;
 private boolean isalife;
 // 图片帧数组的下标
 private int frameindex = 0;
 // 一个标记通过此标记确定此处是否有植物
 private int mapindex;
 // 控制产生子弹的时间
 private long lastbirthtime;
 // 摆动速度控制,两帧一动
 private boolean swingspeed;
 public pea(int locationx, int locationy, int mapindex) {
  this.locationx = locationx;
  this.locationy = locationy;
  this.mapindex = mapindex;
  isalife = true;
  swingspeed = false;
 }
 @override
 public void drawself(canvas canvas, paint paint) {
  // todo auto-generated method stub
  if (isalife) {
   // 这里绘入的bitmap就需要在绘制自己的时候换自己的帧动画以形成动态效果
   // 这个组在config中已经初始化好了
   canvas.drawbitmap(config.peaframes[frameindex], locationx,
     locationy, paint);
   // 用此变量让数组变化
   // 通过这样的取模方法,可以让这个frameindex值不超过7
   // 当frameindex为8时会变为0,避免数组越界
   if (!swingspeed) {
    frameindex = (++frameindex) % 8;
    swingspeed = false;
   } else {
    swingspeed = true;
   }
   // 用此处判断来确定每10秒一个子弹的产生
   if (system.currenttimemillis() - lastbirthtime > 10000) {
    lastbirthtime = system.currenttimemillis();
    givebirth2bullet();
   }
  }
 }
 // 产生子弹
 // 子弹具有生命,然后两种情况,被点击则转换状态,移动到上方子弹的标志处,过一段时间不点击则死亡消失
 // 产生在花的位置上
 private void givebirth2bullet() {
  // 首先要有子弹的图层集合,处于第三层,那么就需要操作集合,就需要调用gameview.getinstance
  gameview.getinstance().givebirth2bullet(locationx, locationy);
 }
 @override
 public int getmodelwidth() {
  // todo auto-generated method stub
  return config.peaframes[0].getwidth();
 }
 public int getlocationx() {
  return locationx;
 }
 public void setlocationx(int locationx) {
  this.locationx = locationx;
 }
 public int getlocationy() {
  return locationy;
 }
 public void setlocationy(int locationy) {
  this.locationy = locationy;
 }
 public boolean isalife() {
  return isalife;
 }
 public void setalife(boolean isalife) {
  this.isalife = isalife;
 }
 @override
 public int getmapindex() {
  // todo auto-generated method stub
  return mapindex;
 }
}
package com.example.liu.mygame.entity;
import android.graphics.canvas;
import android.graphics.paint;
import android.graphics.rect;
import android.util.log;
import android.view.motionevent;
import com.example.liu.mygame.global.config;
import com.example.liu.mygame.model.basemodel;
import com.example.liu.mygame.model.touchable;
import com.example.liu.mygame.view.gameview;
public class seedflower extends basemodel implements touchable {
 private int locationx;
 private int locationy;
 // 生命
 private boolean isalife;
 // 触摸区域(矩形)
 private rect toucharea;
 public seedflower(int locationx, int locationy) {
  this.locationx = locationx;
  this.locationy = locationy;
  this.isalife = true;
  // 初始化触摸响应矩形区域
  toucharea = new rect(locationx, locationy, locationx
    + config.seedflower.getwidth(), locationy
    + config.seedflower.getheight());
 }
 @override
 public void drawself(canvas canvas, paint paint) {
  // todo auto-generated method stub
  if (isalife) {
   canvas.drawbitmap(config.seedflower, locationx, locationy, paint);
  }
 }
 @override
 public boolean ontouch(motionevent event) {
  // todo auto-generated method stub
  // 获取并传入触摸的x,y坐标,getx() gety()获取到的数据都是float型
  int x = (int) event.getx();
  int y = (int) event.gety();
  if (toucharea.contains(x, y)) {
   // 当触摸点落在区域内则响应
   // 生成安置状态的花(优先级最高)
   if (config.sunlight >= 50) {
    applay4emplaceflower();
    return true;
   }
  }
  return false;
 }
 // 通过gameview来请求生成一个安置状态的花(优先级最高)
 private void applay4emplaceflower() {
  // todo auto-generated method stub
  gameview.getinstance().applay4emplaceplant(locationx, locationy, this);
 }
 public int getlocationx() {
  return locationx;
 }
 public void setlocationx(int locationx) {
  this.locationx = locationx;
 }
 public int getlocationy() {
  return locationy;
 }
 public void setlocationy(int locationy) {
  this.locationy = locationy;
 }
 public boolean isalife() {
  return isalife;
 }
 public void setalife(boolean isalife) {
  this.isalife = isalife;
 }
}
package com.example.liu.mygame.entity;
import android.graphics.canvas;
import android.graphics.paint;
import android.graphics.rect;
import android.util.log;
import android.view.motionevent;
import com.example.liu.mygame.global.config;
import com.example.liu.mygame.model.basemodel;
import com.example.liu.mygame.model.touchable;
import com.example.liu.mygame.view.gameview;
public class seedpea extends basemodel implements touchable {
 private int locationx;
 private int locationy;
 // 生命
 private boolean isalife;
 // 触摸区域(矩形)
 private rect toucharea;
 public seedpea(int locationx, int locationy) {
  this.locationx = locationx;
  this.locationy = locationy;
  this.isalife = true;
  // 初始化触摸响应矩形区域
  toucharea = new rect(locationx, locationy, locationx
    + config.seedpea.getwidth(), locationy
    + config.seedpea.getheight());
 }
 @override
 public void drawself(canvas canvas, paint paint) {
  // todo auto-generated method stub
  if (isalife) {
   canvas.drawbitmap(config.seedpea, locationx, locationy, paint);
  }
 }
 @override
 public boolean ontouch(motionevent event) {
  // todo auto-generated method stub
  // 获取并传入触摸的x,y坐标,getx() gety()获取到的数据都是float型
  int x = (int) event.getx();
  int y = (int) event.gety();
  if (toucharea.contains(x, y)) {
   // 当触摸点落在区域内则响应
   // 生成安置状态的豌豆(优先级最高)
   if (config.sunlight >= 100) {
    applay4emplacepea();
    return true;
   }
  }
  return false;
 }
 // 通过gameview来请求生成一个安置状态的豌豆(优先级最高)
 private void applay4emplacepea() {
  // todo auto-generated method stub
  gameview.getinstance().applay4emplaceplant(locationx, locationy, this);
 }
 public int getlocationx() {
  return locationx;
 }
 public void setlocationx(int locationx) {
  this.locationx = locationx;
 }
 public int getlocationy() {
  return locationy;
 }
 public void setlocationy(int locationy) {
  this.locationy = locationy;
 }
 public boolean isalife() {
  return isalife;
 }
 public void setalife(boolean isalife) {
  this.isalife = isalife;
 }
}
package com.example.liu.mygame.entity;
import android.graphics.canvas;
import android.graphics.paint;
import android.graphics.rect;
import android.view.motionevent;
import com.example.liu.mygame.global.config;
import com.example.liu.mygame.model.basemodel;
import com.example.liu.mygame.model.touchable;
public class sun extends basemodel implements touchable {
 // 位置
 private int locationx;
 private int locationy;
 // 生命
 private boolean isalife;
 // 可触摸区域
 private rect toucharea;
 // 阳光产生时间
 private long birthtime;
 // 标示阳光的状态
 private sunstate state;
 // 移动距离
 private int directiondistancex;
 private int directiondistancey;
 // xy方向上的速度分量
 // 根据帧数,确定移动时间,然后来确定移动方式
 private float speedx;
 private float speedy;
 // 用此枚举来标示阳光的状态
 // 两个状态:静止、移动
 // 移动过程中生命周期对阳光无效,静止时生命周期有效
 public enum sunstate {
  show, move
 }
 public sun(int locationx, int locationy) {
  this.locationx = locationx;
  this.locationy = locationy;
  this.isalife = true;
  // 初始化触摸响应矩形区域
  // 对于每个阳光来说能出没的地方只有他这张图片大小的区域
  toucharea = new rect(locationx, locationy, locationx
    + config.sun.getwidth(), locationy + config.sun.getheight());
  // 获取系统时间
  birthtime = system.currenttimemillis();
  // 初始实例化为show状态
  state = sunstate.show;
 }
 @override
 public void drawself(canvas canvas, paint paint) {
  // todo auto-generated method stub
  if (isalife) {
   if (state == sunstate.show) {
    // 判断当前系统时间如果比出生时间大5000毫秒那么阳光生命结束,消失
    if (system.currenttimemillis() - birthtime > config.lifetime) {
     isalife = false;
    }
   } else {// 对于move状态的阳光的处理
    // 移动
    locationx -= speedx;
    locationy -= speedy;
    // 如果图片的y轴坐标移动到超出屏幕或者说移动到与屏幕齐平,那么生命周期结束
    if (locationy <= 0) {
     // 去除阳光
     isalife = false;
     // 改变阳光值
     config.sunlight += 25;
    }
   }
   canvas.drawbitmap(config.sun, locationx, locationy, paint);
  }
 }
 // 触摸事件响应
 @override
 public boolean ontouch(motionevent event) {
  // todo auto-generated method stub
  // 获取触摸点
  int x = (int) event.getx();
  int y = (int) event.gety();
  // 如果触摸点在可触摸区域内
  if (toucharea.contains(x, y)) {
   // 开始运动并且不可被点击,同时可能会与上边框产生碰撞事件
   // 移动过程中也需要时间,如果这个收集时间中用了超过阳光生命值5秒的时间
   // 那么我们需要在点击以后改变阳光的状态并删除原本的静态阳光
   state = sunstate.move;
   // 改变状态以后,那么就要开始移动,移动的起点不一定,但是终点是一定的
   // 移动的终点可以认为是条形框(seedbank)的左上角点
   // 起始点就是此阳光图片的左上角
   // xy方向上的移动距离
   directiondistancex = locationx - config.seedbanklocationx;
   directiondistancey = locationy;
   // 移动速度分量的计算,具体帧数需要项目分析,这里设置为20帧
   speedx = directiondistancex / 20f;
   speedy = directiondistancey / 20f;
   return true;
  }
  return false;
 }
 public int getlocationx() {
  return locationx;
 }
 public void setlocationx(int locationx) {
  this.locationx = locationx;
 }
 public int getlocationy() {
  return locationy;
 }
 public void setlocationy(int locationy) {
  this.locationy = locationy;
 }
 public boolean isalife() {
  return isalife;
 }
 public void setalife(boolean isalife) {
  this.isalife = isalife;
 }
}
package com.example.liu.mygame.entity;
import android.graphics.canvas;
import android.graphics.paint;
import com.example.liu.mygame.global.config;
import com.example.liu.mygame.model.basemodel;
import com.example.liu.mygame.view.gameview;
public class zombie extends basemodel {
 private int locationx;
 private int locationy;
 private boolean isalife;
 // 僵尸位于的跑道,因为此僵尸只跟其所在的跑道内的植物、子弹等进行碰撞检测
 private int raceway;
 // 因为僵尸是移动中的 所以他要有动画帧的下标
 private int frameindex = 0;
 // 移动速度,每一帧移动3像素
 private int peedx = 3;
 public zombie(int locationx, int locationy, int raceway) {
  this.locationx = locationx;
  this.locationy = locationy;
  isalife = true;
  this.raceway = raceway;
 }
 // 在某跑道随机产生僵尸,同时间隔一段时间出现一只僵尸
 @override
 public void drawself(canvas canvas, paint paint) {
  // todo auto-generated method stub
  if (locationx < 0) {
   config.game = false;
  }
  if (isalife) {
   canvas.drawbitmap(config.zombieframes[frameindex], locationx,
     locationy, paint);
   frameindex = (++frameindex) % 7;
   locationx -= peedx;
   // 碰撞检测,僵尸发起的此碰撞检测
   gameview.getinstance().checkcollision(this, raceway);
  }
 }
 @override
 public int getmodelwidth() {
  // todo auto-generated method stub
  return config.zombieframes[0].getwidth();
 }
 public int getlocationx() {
  return locationx;
 }
 public void setlocationx(int locationx) {
  this.locationx = locationx;
 }
 public int getlocationy() {
  return locationy;
 }
 public void setlocationy(int locationy) {
  this.locationy = locationy;
 }
 public boolean isalife() {
  return isalife;
 }
 public void setalife(boolean isalife) {
  this.isalife = isalife;
 }
}
package com.example.liu.mygame.entity;
import android.graphics.canvas;
import android.graphics.paint;
import com.example.liu.mygame.model.basemodel;
import com.example.liu.mygame.view.gameview;
public class zombiemanager extends basemodel {
 // 一般需要显示出现在屏幕上的实体才需要继承basemodel
 // 所以此处的僵尸控制器其实不需要继承basemodel
 // 但是为了与之前的flower和pea产生器相统一
 // 效仿以前的模式减少工作量
 // 在这里也进行继承
 private boolean isalife;
 // 最后一只僵尸的产生时间
 private long lastbirthtime;
 public zombiemanager() {
  lastbirthtime = system.currenttimemillis();
  isalife = true;
 }
 @override
 public void drawself(canvas canvas, paint paint) {
  // todo auto-generated method stub
  // 此处不需要绘制出图片,所以不需要draw,但是可以进行逻辑上的处理
  if (system.currenttimemillis() - lastbirthtime > 15000) {
   lastbirthtime = system.currenttimemillis();
   givebirth2zombie();
  }
 }
 private void givebirth2zombie() {
  // 与gameview请求加入僵尸
  gameview.getinstance().apply4addzombie();
 }
}
package com.example.liu.mygame.global;
import java.util.hashmap;
import android.graphics.bitmap;
import android.graphics.point;
//常量
public class config {
 public static float scalewidth;
 public static float scaleheight;
 public static int devicewidth;
 public static int deviceheight;
 public static bitmap gamebk;
 public static bitmap seedbank;
 public static bitmap gameover;
 // seedbank的位置x坐标
 public static int seedbanklocationx;
 public static bitmap seedflower;
 public static bitmap seedpea;
 // 阳光
 public static bitmap sun;
 // 阳光的生存时间5000毫秒
 public static long lifetime = 5000;
 // 现在的阳光值
 public static int sunlight = 200;
 // 僵尸和植物图片的高度差
 public static int heightydistance;
 // 子弹
 public static bitmap bullet;
 // 将图片帧放入数组
 public static bitmap[] flowerframes = new bitmap[8];
 public static bitmap[] peaframes = new bitmap[8];
 public static bitmap[] zombieframes = new bitmap[7];
 // 放置植物的点
 public static hashmap<integer, point> plantpoints = new hashmap<integer, point>();
 // 跑道
 public static int[] racewayypoints = new int[5];
 // 输赢判断标志
 public static boolean game = true;
}
package com.example.liu.mygame.model;
import android.graphics.canvas;
import android.graphics.paint;
public class basemodel {
 // 基础类,对于所有能展示在屏幕上的动态对象都要继承自此类
 // 位置
 private int locationx;
 private int locationy;
 // 生命
 private boolean isalife;
 // 绘制自己,即移动
 public void drawself(canvas canvas, paint paint) {
 }
 public int getmodelwidth() {
  return 0;
 }
 public int getlocationx() {
  return locationx;
 }
 public void setlocationx(int locationx) {
  this.locationx = locationx;
 }
 public int getlocationy() {
  return locationy;
 }
 public void setlocationy(int locationy) {
  this.locationy = locationy;
 }
 public boolean isalife() {
  return isalife;
 }
 public void setalife(boolean isalife) {
  this.isalife = isalife;
 }
}
package com.example.liu.mygame.model;
// 所有需要种植在地上保持静止的植物都要有这个接口
public interface plant {
 // 用于key
 public int getmapindex();
}
package com.example.liu.mygame.model;
import android.view.motionevent;
public interface touchable {
 // 对于能接受触摸事件的对象的一个公用接口
 // 传入motionevent事件
 public boolean ontouch(motionevent event);
}
package com.example.liu.mygame.tools;
import com.example.liu.mygame.global.config;
import android.app.activity;
import android.content.context;
import android.graphics.bitmap;
import android.graphics.matrix;
import android.util.displaymetrics;
import android.util.log;
public class devicetools {
 private static int[] devicewidthheight = new int[2];
 // 重新设置bitmap的大小
 public static bitmap resizebitmap(bitmap bitmap) {
  if (bitmap != null) {
   int width = bitmap.getwidth();
   int height = bitmap.getheight();
   log.i("info", width + "," + height);
   matrix matrix = new matrix();
   matrix.postscale(config.scalewidth, config.scaleheight);
   bitmap resizedbitmap = bitmap.createbitmap(bitmap, 0, 0, width,
     height, matrix, true);
   return resizedbitmap;
  } else {
   return null;
  }
 }
 // 重载
 // 原因是找到的素材需要进行处理来适应手机屏幕,等比操作,但是如果合成的两张材料图不成比例 那么就不得不用这种重载来适应
 // 首先传入一个bitmap和期望的宽高
 public static bitmap resizebitmap(bitmap bitmap, int w, int h) {
  if (bitmap != null) {
   // 获取传入的图片宽高
   int width = bitmap.getwidth();
   int height = bitmap.getheight();
   // 传入期望的宽高
   int newwidth = w;
   int newheight = h;
   // 缩放比
   float scalewidth = ((float) newwidth) / width;
   float scaleheight = ((float) newheight) / height;
   // 图片矩阵对象,3x3矩阵
   matrix matrix = new matrix();
   // 把缩放比传入期望矩阵
   matrix.postscale(scalewidth, scaleheight);
   // 生成期望的图片
   bitmap resizebitmap = bitmap.createbitmap(bitmap, 0, 0, width,
     height, matrix, true);
   return resizebitmap;
  } else {
   return null;
  }
 }
 // 获取屏幕的宽高
 // 在displaymetrics类中可以获取屏幕的亮度,宽高,刷新率等相关信息
 public static int[] getdeviceinfo(context context) {
  if ((devicewidthheight[0] == 0) && (devicewidthheight[1] == 0)) {
   displaymetrics metrics = new displaymetrics();
   ((activity) context).getwindowmanager().getdefaultdisplay()
     .getmetrics(metrics);
   devicewidthheight[0] = metrics.widthpixels;
   devicewidthheight[1] = metrics.heightpixels;
  }
  return devicewidthheight;
 }
}
package com.example.liu.mygame.view;
import java.util.arraylist;
import com.example.liu.mygame.r;
import com.example.liu.mygame.entity.*;
import com.example.liu.mygame.global.config;
import com.example.liu.mygame.model.basemodel;
import com.example.liu.mygame.model.plant;
import com.example.liu.mygame.model.touchable;
import android.r.bool;
import android.r.integer;
import android.content.context;
import android.graphics.bitmap;
import android.graphics.bitmapfactory;
import android.graphics.canvas;
import android.graphics.color;
import android.graphics.paint;
import android.graphics.point;
import android.graphics.typeface;
import android.util.log;
import android.view.motionevent;
import android.view.surfaceholder;
import android.view.surfaceview;
//对于这种有赛道的游戏可以用以下方法
//对于那种可以上下移动的游戏可以把所有精灵放在一个集合里 不用分图层 每次绘制的时候要按照y坐标进行排序
//对于先画出来的肯定是排在前面的 所以y最小的排在前面即离屏幕上边缘最近的精灵
//那种游戏肯定会通过游戏引擎来进行开发
public class gameview extends surfaceview implements surfaceholder.callback,
  runnable {
 private canvas canvas;
 private paint paint;
 private surfaceholder surfaceholder;
 private boolean gamerunflag;
 private context context;// 用于存放图片地址
 // 把gameview当做总管理,所有的实体都向这里发送请求并处理
 private static gameview gameview;
 private arraylist<basemodel> deadlist;// 存放已消亡的实体,在拖动放手后实体会不显示,但是还存在所以要进行清理
 private arraylist<basemodel> gamelayout3;// 存放第三图层中的实体;
 private arraylist<basemodel> gamelayout2;// 存放第二图层中的实体
 private arraylist<basemodel> gamelayout1;// 存放第一图层中的实体
 // 跑道从上至下
 // 这些可以做一个封装,放入一个for循环进行创建即可
 private arraylist<basemodel> gamelayout4plant0;
 private arraylist<basemodel> gamelayout4plant1;
 private arraylist<basemodel> gamelayout4plant2;
 private arraylist<basemodel> gamelayout4plant3;
 private arraylist<basemodel> gamelayout4plant4;
 // 定义僵尸跑道
 private arraylist<basemodel> gamelayout4zombie0;
 private arraylist<basemodel> gamelayout4zombie1;
 private arraylist<basemodel> gamelayout4zombie2;
 private arraylist<basemodel> gamelayout4zombie3;
 private arraylist<basemodel> gamelayout4zombie4;
 // 定义僵尸控制器,通过此控制器来使僵尸实现移动
 private zombiemanager zombiemanager;
 public gameview(context context) {
  super(context);
  // todo gameview
  this.context = context;
  paint = new paint();
  surfaceholder = getholder();
  surfaceholder.addcallback(this);
  gamerunflag = true;
  gameview = this;
  if (config.game == false) {
   canvas.drawbitmap(config.gameover, 0, 0, paint);
  }
 }
 @override
 public void surfacecreated(surfaceholder holder) {
  // todo surfacecreated
  // 加载bitmap(图片)
  createelement();
  new thread(this).start();
 }
 private void createelement() {
  // todo createelement
  // 给植物与僵尸的高度差赋值
  config.heightydistance = config.zombieframes[0].getheight()
    - config.flowerframes[0].getheight();
  // 给seedbank的x坐标赋初值
  config.seedbanklocationx = (config.devicewidth - config.seedbank
    .getwidth()) / 2;
  // 初始化第三图层
  gamelayout3 = new arraylist<basemodel>();
  // 当此方法被触发时便会创建卡片对象
  gamelayout2 = new arraylist<basemodel>();
  seedflower seedflower = new seedflower(
    (config.devicewidth - config.seedbank.getwidth()) / 2
      + config.seedflower.getwidth() / 3
      + config.seedbank.getwidth() / 7,
    config.seedbank.getheight() / 10);
  seedpea seedpea = new seedpea(
    (config.devicewidth - config.seedbank.getwidth()) / 2
      + config.seedflower.getwidth() / 7
      + config.seedbank.getwidth() / 7 * 2,
    config.seedbank.getheight() / 10);
  gamelayout2.add(seedflower);
  gamelayout2.add(seedpea);
  // 添加安置状态中的植物
  gamelayout1 = new arraylist<basemodel>();
  deadlist = new arraylist<basemodel>();
  gamelayout4plant0 = new arraylist<basemodel>();
  gamelayout4plant1 = new arraylist<basemodel>();
  gamelayout4plant2 = new arraylist<basemodel>();
  gamelayout4plant3 = new arraylist<basemodel>();
  gamelayout4plant4 = new arraylist<basemodel>();
  // 僵尸跑道初始化
  gamelayout4zombie0 = new arraylist<basemodel>();
  gamelayout4zombie1 = new arraylist<basemodel>();
  gamelayout4zombie2 = new arraylist<basemodel>();
  gamelayout4zombie3 = new arraylist<basemodel>();
  gamelayout4zombie4 = new arraylist<basemodel>();
  // 初始化僵尸控制器
  zombiemanager = new zombiemanager();
  // 放置植物的合适位置
  for (int i = 0; i < 5; i++) {
   for (int j = 0; j < 9; j++) {
    config.plantpoints.put(i * 10 + j, new point(
      (j + 2) * config.devicewidth / 11 - config.devicewidth
        / 11 / 2, (i + 1) * config.deviceheight / 6));
    if (j == 0) {
     config.racewayypoints[i] = (i + 1) * config.deviceheight
       / 6;
    }
   }
  }
 }
 @override
 public void surfacechanged(surfaceholder holder, int format, int width,
   int height) {
  // todo surfacechanged
 }
 @override
 public void surfacedestroyed(surfaceholder holder) {
  // todo surfacedestroyed
 }
 // 所有的动画帧都由这个run方法来控制
 // 控制动画帧的时候要注意首先进行数据更新 然后在更新图像
 @override
 public void run() {
  // todo run
  while (gamerunflag) {
   synchronized (surfaceholder) {
    try {
     // 为了形成动画效果首先需要清理屏幕
     // 加锁避免很多线程同时绘制
     canvas = surfaceholder.lockcanvas();
     // 绘入背景,最底层图层
     canvas.drawbitmap(config.gamebk, 0, 0, paint);
     // 绘入上方植物栏,倒数第二层图层,仅覆盖于背景之上
     canvas.drawbitmap(config.seedbank,
       config.seedbanklocationx, 0, paint);
     // 数据更改操作
     updatedata();
     // 绘入植物卡片(第二层)
     ondraw(canvas);
    } catch (exception e) {
     // todo: handle exception
    } finally {
     // 解锁并提交
     surfaceholder.unlockcanvasandpost(canvas);
     // canvasandpost必须要进行解锁 不管程序有什么问题必须给用户直观完整的显示过程
     // 以防万一的话 加入try catch
    }
   }
   // 加入以下语句每次循环中休眠50毫秒减少一直循环的系统资源浪费
   // 使用50毫秒的原因是在42帧及以上肉眼就会认为是流畅的,即1秒42张图片,每次循环休眠50毫秒即20帧
   // 如果把sleep放在synchronized中的话会出现程序每次遍历完立刻睡眠然后再次遍历没有给其他进程事件运行会造成卡死
   try {
    thread.sleep(40);
   } catch (interruptedexception e) {
    // todo auto-generated catch block
    e.printstacktrace();
   }
  }
 }
 private void updatedata() {
  // 在此方法中进行数据更新
  // 清除deadlist
  deadlist.clear();
  // 遍历第一图层
  for (basemodel model : gamelayout1) {
   if (!model.isalife()) {
    deadlist.add(model);
   }
  }
  // 遍历第二图层
  for (basemodel model : gamelayout2) {
   if (!model.isalife()) {
    deadlist.add(model);
   }
  }
  // 遍历第三图层
  for (basemodel model : gamelayout3) {
   if (!model.isalife()) {
    deadlist.add(model);
   }
  }
  // 遍历五条跑道上的僵尸
  for (basemodel model : gamelayout4zombie0) {
   if (!model.isalife()) {
    deadlist.add(model);
   }
  }
  for (basemodel model : gamelayout4zombie1) {
   if (!model.isalife()) {
    deadlist.add(model);
   }
  }
  for (basemodel model : gamelayout4zombie2) {
   if (!model.isalife()) {
    deadlist.add(model);
   }
  }
  for (basemodel model : gamelayout4zombie3) {
   if (!model.isalife()) {
    deadlist.add(model);
   }
  }
  for (basemodel model : gamelayout4zombie4) {
   if (!model.isalife()) {
    deadlist.add(model);
   }
  }
  // 遍历五条跑道上的植物
  for (basemodel model : gamelayout4plant0) {
   if (!model.isalife()) {
    deadlist.add(model);
   }
  }
  for (basemodel model : gamelayout4plant1) {
   if (!model.isalife()) {
    deadlist.add(model);
   }
  }
  for (basemodel model : gamelayout4plant2) {
   if (!model.isalife()) {
    deadlist.add(model);
   }
  }
  for (basemodel model : gamelayout4plant3) {
   if (!model.isalife()) {
    deadlist.add(model);
   }
  }
  for (basemodel model : gamelayout4plant4) {
   if (!model.isalife()) {
    deadlist.add(model);
   }
  }
  // 遍历deadlist集合
  for (basemodel model : deadlist) {
   // 在各个图层列表中把它们移除
   gamelayout1.remove(model);
   gamelayout2.remove(model);
   gamelayout3.remove(model);
   gamelayout4zombie0.remove(model);
   gamelayout4zombie1.remove(model);
   gamelayout4zombie2.remove(model);
   gamelayout4zombie3.remove(model);
   gamelayout4zombie4.remove(model);
  }
 }
 private void ondraw(canvas canvas) {
  // todo ondraw
  // 在此方法中进行绘图作业
  // 按照游戏的层次进行绘制,先画游戏层次最下方的精灵
  // 按照已经写好的分层顺序
  // 绘制出阳光值
  paint paint2 = new paint();
  paint2.settypeface(typeface.default_bold);
  paint2.settextsize(15);
  canvas.drawtext(config.sunlight + "", config.devicewidth * 2 / 7,
    config.deviceheight / 8, paint2);
  // 僵尸控制器中的drawself实现僵尸移动
  zombiemanager.drawself(canvas, paint);
  // 跑道应该处于第四层故放在上方先绘制出来
  // 遍历五条跑道调用drawself方法进行绘制植物
  // 此处也可以进行方法的抽象 或者说应该把这些重复的代码抽象为一个方法调用不同的值进去
  for (basemodel model : gamelayout4plant0) {
   model.drawself(canvas, paint);
  }
  for (basemodel model : gamelayout4plant1) {
   model.drawself(canvas, paint);
  }
  for (basemodel model : gamelayout4plant2) {
   model.drawself(canvas, paint);
  }
  for (basemodel model : gamelayout4plant3) {
   model.drawself(canvas, paint);
  }
  for (basemodel model : gamelayout4plant4) {
   model.drawself(canvas, paint);
  }
  // 第三层(阳光)
  for (basemodel model : gamelayout3) {
   model.drawself(canvas, paint);
  }
  // 第二层
  for (basemodel model : gamelayout2) {
   model.drawself(canvas, paint);
  }
  // 遍历五条跑道调用drawself方法进行绘制僵尸
  // 此处也可以进行方法的抽象 或者说应该把这些重复的代码抽象为一个方法调用不同的值进去
  // 第二层是植物卡片,僵尸在经过第一行的时候应该可以挡住植物卡片
  for (basemodel model : gamelayout4zombie0) {
   model.drawself(canvas, paint);
  }
  for (basemodel model : gamelayout4zombie1) {
   model.drawself(canvas, paint);
  }
  for (basemodel model : gamelayout4zombie2) {
   model.drawself(canvas, paint);
  }
  for (basemodel model : gamelayout4zombie3) {
   model.drawself(canvas, paint);
  }
  for (basemodel model : gamelayout4zombie4) {
   model.drawself(canvas, paint);
  }
  // 第一层
  // gamelayout1比gamelayout2的层次要高故放在后面
  for (basemodel model : gamelayout1) {
   model.drawself(canvas, paint);
  }
  /*
   * private m=200; paint paint3 = new paint(); paint3.setalpha(100);
   * canvas.drawrect(100, 100, 200, m, paint3); m-=5; 设置半透明效果
   * m的作用是可以让这个半透明效果逐步消去, m的变化大小就可以理解为此植物的冷却时间
   */
 }
 // 在这里重写触摸响应事件
 @override
 public boolean ontouchevent(motionevent event) {
  // todo ontouchevent
  // 对于相应来说gamelayout1的优先级最高故放在gamelayout2上方
  for (basemodel model : gamelayout1) {
   // 判定是否为touchable的子类,只有是touchable的子类才能响应
   if (model instanceof touchable) {
    // 然后进行ontouch事件,查看是否被点击,如果点击那么返回true
    if (((touchable) model).ontouch(event)) {
     return true;
    }
   }
  }
  // 遍历第二层中的全部实体
  for (basemodel model : gamelayout2) {
   // 判定是否为touchable的子类,只有是touchable的子类才能响应
   if (model instanceof touchable) {
    // 然后进行ontouch事件,查看是否被点击,如果点击那么返回true
    if (((touchable) model).ontouch(event)) {
     return true;
    }
   }
  }
  // 遍历第三层中的全部实体
  for (basemodel model : gamelayout3) {
   // 判定是否为touchable的子类,只有是touchable的子类才能响应
   if (model instanceof touchable) {
    // 然后进行ontouch事件,查看是否被点击,如果点击那么返回true
    if (((touchable) model).ontouch(event)) {
     return true;
    }
   }
  }
  return false;
 }
 // 获取gameview的方法,让gameview编程所有实体的总桥梁
 public static gameview getinstance() {
  return gameview;
 }
 // 添加emplaceplant植物(优先级最高)
 public void applay4emplaceplant(int locationx, int locationy,
   basemodel model) {
  // todo applay4emplaceplant
  // 相当于在进行数据更新,在ondraw中会从这里取出一个个元素进行绘制,绘制过程中如果这里还会有更新那么会产生冲突,所以需要在这里加入一个同步锁
  // 所有对于集合的操作都要加入同步锁,锁对象用surfaceholder当得到此surfaceholder锁对象的时候才能够进行操作
  synchronized (surfaceholder) {
   // gamelayout1放的是正在安放状态的植物,没有放下
   // new一个处于安置状态的实体
   // gamelayout1中只能有0~1个实例
   if (gamelayout1.size() < 1) {
    if (model instanceof seedpea) {
     gamelayout1.add(new emplacepea(locationx, locationy));
    } else {
     gamelayout1.add(new emplaceflower(locationx, locationy));
    }
   }
  }
 }
 public void applay4plant(int locationx, int locationy, basemodel basemodel) {
  // todo applay4plant
  // 安放静态植物
  // 以空间换时间,因为植物、子弹、僵尸、都在第四层所以对于这些来说把它们分为五个赛道从上至下五层
  // 每条赛道上有两个集合,一个是僵尸的集合,另一个是植物与子弹的集合,这样分是为了碰撞检测
  // 为了减少碰撞检测事件去除部分不必要的运算,故而分成很多层
  // 循环这个可安放植物的hashmap,目的是拿出每个元素与locationx和locationy进行比较
  // key的作用是让每个paint的标示不同
  synchronized (surfaceholder) {// 加锁
   point point;
   for (integer key : config.plantpoints.keyset()) {
    // 找距离locationx与locationy最近而且处于目标地域上
    point = config.plantpoints.get(key);
    if ((math.abs(locationx - point.x) < config.devicewidth / 11 / 2)
      && (math.abs(locationy - point.y) < config.deviceheight / 6 / 2)) {
     // 跑道标示
     int racewayindex = 6;
     for (int i = 0; i < config.racewayypoints.length; i++) {
      // 遍历跑道的y值
      if (point.y == config.racewayypoints[i]) {
       // 如果y值相等那么跑道确定
       racewayindex = i;
      }
     }
     if (isplantexist(key, racewayindex)) {
      // 跑道数出发事件
      switch (racewayindex) {
      case 0:
       if (basemodel instanceof emplacepea) {
        gamelayout4plant0.add(new pea(point.x, point.y,
          key));
        config.sunlight -= 100;
       } else {
        gamelayout4plant0.add(new flower(point.x,
          point.y, key));
        config.sunlight -= 50;
       }
       break;
      case 1:
       if (basemodel instanceof emplacepea) {
        gamelayout4plant1.add(new pea(point.x, point.y,
          key));
        config.sunlight -= 100;
       } else {
        gamelayout4plant1.add(new flower(point.x,
          point.y, key));
        config.sunlight -= 50;
       }
       break;
      case 2:
       if (basemodel instanceof emplacepea) {
        gamelayout4plant2.add(new pea(point.x, point.y,
          key));
        config.sunlight -= 100;
       } else {
        gamelayout4plant2.add(new flower(point.x,
          point.y, key));
        config.sunlight -= 50;
       }
       break;
      case 3:
       if (basemodel instanceof emplacepea) {
        gamelayout4plant3.add(new pea(point.x, point.y,
          key));
        config.sunlight -= 100;
       } else {
        gamelayout4plant3.add(new flower(point.x,
          point.y, key));
        config.sunlight -= 50;
       }
       break;
      case 4:
       if (basemodel instanceof emplacepea) {
        gamelayout4plant4.add(new pea(point.x, point.y,
          key));
        config.sunlight -= 100;
       } else {
        gamelayout4plant4.add(new flower(point.x,
          point.y, key));
        config.sunlight -= 50;
       }
       break;
      default:
       break;
      }
     }
    }
   }
  }
 }
 // 判断此处是否已经有植物的方法
 // key是标示,racewayindex用于确定应查哪一个跑道
 private boolean isplantexist(int key, int racewayindex) {
  switch (racewayindex) {
  case 0:
   for (basemodel model : gamelayout4plant0) {
    // 首先 如果这个区域是继承了plant接口的子类(因为子弹和阳光不继承plant做以区别)
    if (model instanceof plant) {
     // 然后此处的key与传入的key相等
     if (key == ((plant) model).getmapindex()) {
      // 那么返回此处不能种植植物
      return false;
     }
    }
   }
   break;
  case 1:
   for (basemodel model : gamelayout4plant1) {
    // 首先 如果这个区域是继承了plant接口的子类(因为子弹和阳光不继承plant做以区别)
    if (model instanceof plant) {
     // 然后此处的key与传入的key相等
     if (key == ((plant) model).getmapindex()) {
      // 那么返回此处不能种植植物
      return false;
     }
    }
   }
   break;
  case 2:
   for (basemodel model : gamelayout4plant2) {
    // 首先 如果这个区域是继承了plant接口的子类(因为子弹和阳光不继承plant做以区别)
    if (model instanceof plant) {
     // 然后此处的key与传入的key相等
     if (key == ((plant) model).getmapindex()) {
      // 那么返回此处不能种植植物
      return false;
     }
    }
   }
   break;
  case 3:
   for (basemodel model : gamelayout4plant3) {
    // 首先 如果这个区域是继承了plant接口的子类(因为子弹和阳光不继承plant做以区别)
    if (model instanceof plant) {
     // 然后此处的key与传入的key相等
     if (key == ((plant) model).getmapindex()) {
      // 那么返回此处不能种植植物
      return false;
     }
    }
   }
   break;
  case 4:
   for (basemodel model : gamelayout4plant4) {
    // 首先 如果这个区域是继承了plant接口的子类(因为子弹和阳光不继承plant做以区别)
    if (model instanceof plant) {
     // 然后此处的key与传入的key相等
     if (key == ((plant) model).getmapindex()) {
      // 那么返回此处不能种植植物
      return false;
     }
    }
   }
   break;
  default:
   break;
  }
  return true;
 }
 // 被flower请求,用于产生阳光
 public void givebrith2sun(int locationx, int locationy) {
  // todo givebrith2sun
  // 先设置锁住
  synchronized (surfaceholder) {
   gamelayout3.add(new sun(locationx, locationy));
  }
 }
 // 被pea请求,用于产生子弹
 public void givebirth2bullet(int locationx, int locationy) {
  // todo auto-generated method stub
  // 先设置锁住
  synchronized (surfaceholder) {// 加锁
   point point;
   for (integer key : config.plantpoints.keyset()) {
    // 找距离locationx与locationy最近而且处于目标地域上
    point = config.plantpoints.get(key);
    if ((math.abs(locationx - point.x) < config.devicewidth / 11 / 2)
      && (math.abs(locationy - point.y) < config.deviceheight / 6 / 2)) {
     // 跑道标示
     int racewayindex = 6;
     for (int i = 0; i < config.racewayypoints.length; i++) {
      // 遍历跑道的y值
      if (point.y == config.racewayypoints[i]) {
       // 如果y值相等那么跑道确定
       racewayindex = i;
      }
     }
     switch (racewayindex) {
     case 0:
      gamelayout4plant0.add(new bullet(locationx, locationy));
      break;
     case 1:
      gamelayout4plant1.add(new bullet(locationx, locationy));
      break;
     case 2:
      gamelayout4plant2.add(new bullet(locationx, locationy));
      break;
     case 3:
      gamelayout4plant3.add(new bullet(locationx, locationy));
      break;
     case 4:
      gamelayout4plant4.add(new bullet(locationx, locationy));
      break;
     default:
      break;
     }
    }
   }
  }
 }
 // 僵尸控制器产生相应,加入僵尸
 public void apply4addzombie() {
  // todo apply4addzombie
  // 先解锁
  synchronized (surfaceholder) {
   int raceway = 0;
   // 0~4的随机数来进行跑到初始化
   // math.random()产生的是0~1的不包括1的随机double型数字
   raceway = (int) (math.random() * 5);
   // config.devicewidth + 20是为了让僵尸逐步走入屏幕
   // config.racewayypoints[raceway] - config.heightydistance
   // 是为了让僵尸与植物的底对齐
   switch (raceway) {
   case 0:
    gamelayout4zombie0
      .add(new zombie(config.devicewidth + 20,
        config.racewayypoints[raceway]
          - config.heightydistance, raceway));
    break;
   case 1:
    gamelayout4zombie1
      .add(new zombie(config.devicewidth + 20,
        config.racewayypoints[raceway]
          - config.heightydistance, raceway));
    break;
   case 2:
    gamelayout4zombie2
      .add(new zombie(config.devicewidth + 20,
        config.racewayypoints[raceway]
          - config.heightydistance, raceway));
    break;
   case 3:
    gamelayout4zombie3
      .add(new zombie(config.devicewidth + 20,
        config.racewayypoints[raceway]
          - config.heightydistance, raceway));
    break;
   case 4:
    gamelayout4zombie4
      .add(new zombie(config.devicewidth + 20,
        config.racewayypoints[raceway]
          - config.heightydistance, raceway));
    break;
   default:
    break;
   }
  }
 }
 // 处理碰撞检测,碰撞检测的发起者是僵尸
 public void checkcollision(zombie zombie, int raceway) {
  // todo auto-generated method stub
  synchronized (surfaceholder) {
   switch (raceway) {
   case 0:
    for (basemodel model : gamelayout4plant0) {
     if (math.abs((model.getlocationx() + model.getmodelwidth() / 2)
       - (zombie.getlocationx() + zombie.getmodelwidth() / 2)) < math
       .abs((model.getmodelwidth() + zombie
         .getmodelwidth()) / 2)) {
      if (model instanceof plant) {
       // 植物死
       model.setalife(false);
      } else if (model instanceof bullet) {
       // 子弹死
       model.setalife(false);
       // 僵尸死
       zombie.setalife(false);
       model.setalife(true);
      }
     }
    }
    break;
   case 1:
    for (basemodel model : gamelayout4plant1) {
     if (math.abs((model.getlocationx() + model.getmodelwidth() / 2)
       - (zombie.getlocationx() + zombie.getmodelwidth() / 2)) < math
       .abs((model.getmodelwidth() + zombie
         .getmodelwidth()) / 2)) {
      if (model instanceof plant) {
       // 植物死
       model.setalife(false);
      } else if (model instanceof bullet) {
       // 子弹死
       model.setalife(false);
       // 僵尸死
       zombie.setalife(false);
       model.setalife(true);
      }
     }
    }
    break;
   case 2:
    for (basemodel model : gamelayout4plant2) {
     if (math.abs((model.getlocationx() + model.getmodelwidth() / 2)
       - (zombie.getlocationx() + zombie.getmodelwidth() / 2)) < math
       .abs((model.getmodelwidth() + zombie
         .getmodelwidth()) / 2)) {
      if (model instanceof plant) {
       // 植物死
       model.setalife(false);
      } else if (model instanceof bullet) {
       // 子弹死
       model.setalife(false);
       // 僵尸死
       zombie.setalife(false);
       model.setalife(true);
      }
     }
    }
    break;
   case 3:
    for (basemodel model : gamelayout4plant3) {
     if (math.abs((model.getlocationx() + model.getmodelwidth() / 2)
       - (zombie.getlocationx() + zombie.getmodelwidth() / 2)) < math
       .abs((model.getmodelwidth() + zombie
         .getmodelwidth()) / 2)) {
      if (model instanceof plant) {
       // 植物死
       model.setalife(false);
      } else if (model instanceof bullet) {
       // 子弹死
       model.setalife(false);
       // 僵尸死
       zombie.setalife(false);
       model.setalife(true);
      }
     }
    }
    break;
   case 4:
    for (basemodel model : gamelayout4plant4) {
     if (math.abs((model.getlocationx() + model.getmodelwidth() / 2)
       - (zombie.getlocationx() + zombie.getmodelwidth() / 2)) < math
       .abs((model.getmodelwidth() + zombie
         .getmodelwidth()) / 2)) {
      if (model instanceof plant) {
       // 植物死
       model.setalife(false);
      } else if (model instanceof bullet) {
       // 子弹死
       model.setalife(false);
       // 僵尸死
       zombie.setalife(false);
       model.setalife(true);
      }
     }
    }
    break;
   default:
    break;
   }
  }
 }
}

Android植物大战僵尸小游戏

Android植物大战僵尸小游戏

延伸 · 阅读

精彩推荐