项目源码:https://gitee.com/miwenwen/lottery-system/tree/dev/MonsterGame
项目概述
项目中主要设置了怪物属性,玩家属性
该项目是一个基于鸿蒙操作系统的打怪小游戏,通过简单的游戏机制与图形设计,玩家可以操控角色与怪物进行战斗。游戏设计目的是为了巩固这种学期学习的鸿蒙开发的基本技巧,包括应用的结构设计、界面设计、事件处理以及基础的动画效果。
目标与功能
本游戏的核心功能是让玩家通过控制角色击败怪物,逐步提升角色等级和技能,并完成一系列的任务。玩家可以与怪物战斗、升级角色、解锁新技能以及获取奖励等功能
游戏玩法
玩家进入游戏,点击攻击,怪物的生命数和玩家的生命数相应减少,打败怪物后,玩家的金币数会增加。当玩家攒够金币后,可以到商店里购买一些属性(包括,生命值,攻击,防御等)。玩家还可以更换不同的怪物进行游戏。在游戏过程中,玩家可以选择保存,读取,退出。
保存,把当前游戏的状态保存带存档页面。
读取,可以读取存档页面的记录,继续进行游戏。
初始数据
怪物数据
export class Monster {
//属性
id: number
Name: string
Image: Resource
//战斗
Health: number
Attack: number
Defense: number
//金币
Gold: number
constructor(id: number, Name: string, Image: Resource, Health: number,
Attack: number, Defense: number, Gold: number) {
this.id = id
this.Name = Name
this.Image = Image
this.Health = Health
this.Attack = Attack
this.Defense = Defense
this.Gold = Gold
}
}
**玩家数据**
```typescript
class Player{
//属性
PlayerHealth:number = 1000
PlayerAttack:number = 10
PlayerDefense:number = 10
//金币
PlayerGold:number = 0
}
const player = new Player()
export default player as Player
怪物数据列表
let monster01 = new Monster(0,"绿色史莱姆",$r('app.media.smileGreen'),35,18,1,1)
let monster02 = new Monster(1,"红色史莱姆",$r('app.media.smileRed'),45,20,1,2)
let monster03 = new Monster(2,"蝙蝠",$r('app.media.bat'),35,38,3,3)
let monster04 = new Monster(3,"初级法师",$r('app.media.mage'),60,32,8,5)
let monster05 = new Monster(4,"骷髅人",$r('app.media.boner'),50,42,6,6)
export class DataMonster {
MonsterList :Array<Monster> = [
monster01,monster02,monster03,monster04,monster05
]
}
const dataMonster = new DataMonster()
export default dataMonster as DataMonster
主页面UI
Column() {
Blank()
Text("魔塔(伪)").fontSize(60)
.fontColor(Color.Orange)
.margin({bottom:50})
Column(){
Text("重新开始").fontSize(35).fontColor(Color.Blue)
.onClick(()=>{
router.replaceUrl({
url:"pages/GamePage"
})
})
Text("继续游戏").fontSize(35).fontColor(Color.Blue)
.onClick(()=>{
router.replaceUrl({
url:"pages/SavePage",
params:{
source:"load"
}
})
})
Text("退出游戏").fontSize(35).fontColor(Color.Blue)
}.height("30%").width("60%")
.opacity(0.5)
.justifyContent(FlexAlign.SpaceEvenly)
.backgroundColor(Color.Gray)
Blank()
}.width("100%").height("100%")
.backgroundColor("#ff999b9f")
游戏界面
对战界面
Column() {
}.width("100%").height("100%")
.backgroundColor("#ffa9acaf")
@Extend(Text) function textStyle(){
.fontSize(20).width("100%").textAlign(TextAlign.Center)
}
怪物图案和属性
//怪物信息
@State monsterId: number = 0
@State monsterHealth: number = 0
//......
Row() {
Column() {
Row() {
Text("怪物:").fontSize(20) Text(dataMonster.MonsterList[this.monsterId].Name.toString()).textStyle()
}.width("80%")
Row() {
Text("生命:").fontSize(20)
Text(this.monsterHealth.toString())
.textStyle()
}.width("80%")
Row() {
Text("攻击:").fontSize(20) Text(dataMonster
.MonsterList[this.monsterId].Attack.toString())
.textStyle()
}.width("80%")
Row() {
Text("防御:").fontSize(20) Text(dataMonster
.MonsterList[this.monsterId].Defense.toString())
.textStyle()
}.width("80%")
}
.width(200)
.height("90%")
.justifyContent(FlexAlign.SpaceEvenly)
.backgroundColor(Color.Gray)
.opacity(0.5)
}.height("120").width("90%")
.justifyContent(FlexAlign.End)
Row() {
Image(dataMonster.MonsterList[this.monsterId].Image).width(100).height(100)
}.width("80%")
.margin({ top: 30 })
玩家图案和操作
Blank()
Row() {
Column() {
Button("攻击")
.fontSize(26)
.onClick(() => {
this.attackEvent()
}).backgroundColor("")
.fontColor(Color.Red)
Button("更换")
.fontSize(26).backgroundColor("")
.fontColor(Color.Blue)
.onClick(() => {
this.isMonsterListShow = Visibility.Visible
})
Button("商店")
.fontColor(Color.Green)
.fontSize(26).backgroundColor("")
.onClick(()=>{
this.isShopShow =Visibility.Visible
})
}.width("50%").height("100%")
Image($r('app.media.player')).width(120).height(120)
}.width("80%").height(120).justifyContent(FlexAlign.SpaceBetween)
玩家属性及系统
//玩家信息
@State playerHealth: number = 0
@State playerAttack: number = 0
@State playerDefense: number = 0
@State playerGold: number = 0
@State playerHited: number = 0
//......
Row() {
Column() {
Row() {
Text("生命:").fontSize(20)
Text(this.playerHealth.toString())
.textStyle()
}.width("90%")
Row() {
Text("攻击:").fontSize(20)
Text(this.playerAttack.toString())
.textStyle()
}.width("90%")
Row() {
Text("防御:").fontSize(20)
Text(this.playerDefense.toString())
.textStyle()
}.width("90%")
Row() {
Text("金币:").fontSize(20)
Text(this.playerGold.toString())
.textStyle()
}.width("90%")
}
.height("80%")
.width("60%")
.justifyContent(FlexAlign.SpaceAround)
.backgroundColor(Color.Gray)
.opacity(0.5)
Column() {
Button("保存").backgroundColor("")
.onClick(()=>{
this.saveRouter()
})
Button("读取").backgroundColor("")
.onClick(()=>{
this.loadRouter()
})
Button("退出").backgroundColor("")
}.height("80%")
.width("40%")
.justifyContent(FlexAlign.SpaceEvenly)
.alignItems(HorizontalAlign.End)
}.width("90%").height(200)
控制按钮
攻击逻辑
//攻击事件
attackEvent() {
let hurt: number = this.playerAttack - dataMonster.MonsterList[this.monsterId].Defense
if (hurt>=this.monsterHealth){
this.monsterHealth = dataMonster.MonsterList[this.monsterId].Health
this.playerGold += dataMonster.MonsterList[this.monsterId].Gold
return
}
setTimeout(() => {
//怪物反击
let result: number = dataMonster.MonsterList[this.monsterId].Attack - this.playerDefense
if (result < 0) {
return
}
this.playerHealth -= result
if (this.playerHealth <= 0) {
console.log("游戏结束")
}
return
}, 500)
//玩家攻击怪物
if (hurt > 0) {
this.monsterHealth -= hurt
}
}
攻击效果
@State playerHited: number = 0
@State monsterHited: number = 0
//....
attackEvent() {
//攻击效果
animateTo({
duration: 100,
curve:Curve.Linear,
iterations: 1,
onFinish: () => {
this.monsterHited = 0
}
}, () => {
this.monsterHited = 0.8
})
//......
setTimeout(() => {
//怪物反击
//攻击效果
animateTo({
duration: 100,
curve:Curve.Linear,
iterations: 1,
onFinish: () => {
this.playerHited = 0
}
}, () => {
this.playerHited = 0.8
})
}, 200)
}
//......
Stack(){ Image(dataMonster.MonsterList[this.monsterId].Image)
.width(100).height(100) Image($r('app.media.hit')).width(60)
.height(60).opacity(this.monsterHited)
}.width(100).height(100)
Stack(){ Image($r('app.media.player')).width(120).height(120)
Image($r('app.media.hit')).width(60).height(60).opacity(this.playerHited)
}.width(120).height(120)
怪物列表
//怪物列表
Column() {
Row() {
Text("头像").width("20%").textAlign(TextAlign.Center).fontSize(20)
Text("名字").width("20%").textAlign(TextAlign.Center).fontSize(20)
Text("生命").width("15%").fontSize(20)
Text("攻击").width("15%").fontSize(20)
Text("防御").width("15%").fontSize(20)
Text("金币").width("15%").fontSize(20)
}.width("100%")
.justifyContent(FlexAlign.SpaceEvenly)
.margin({ top: 20 })
.backgroundColor(Color.Gray)
List() {
ForEach(dataMonster.MonsterList, (monster: Monster) => {
ListItem() {
Row() {
Image(monster.Image).width("20%")
Text(monster.Name).width("20%")
.textAlign(TextAlign.Center).fontSize(20)
Text(monster.Health.toString())
.width("15%").fontSize(20)
Text(monster.Attack.toString())
.width("15%").fontSize(20)
Text(monster.Defense.toString())
.width("15%").fontSize(20)
Text(monster.Gold.toString())
.width("15%").fontSize(20)
}.width("100%").height(120)
.justifyContent(FlexAlign.SpaceEvenly)
.onClick(() => {
this.monsterId = monster.id
this.monsterHealth = monster.Health
this.isMonsterListShow = Visibility.Hidden
})
}
})
}
}
.width("100%")
.height("100%")
.visibility(this.isMonsterListShow)
.backgroundColor(Color.Gray)
.opacity(0.8)
商店
Column() {
Text("商店").fontSize(36).fontWeight(FontWeight.Bold)
.width("100%").textAlign(TextAlign.Center)
.margin({top:20,bottom:30}).fontColor(Color.Blue)
Text("你可以花费10金币,可以任意提升以下属性")
.fontSize(24).width("90%").textAlign(TextAlign.Center)
.margin({bottom:30}).fontColor(Color.Blue)
Text("生命值+400").fontSize(26).width("80%").textAlign(TextAlign.Center)
.margin({top:20}).fontColor(Color.Blue)
.onClick(()=>{
if (this.playerGold<10){
return
}
this.playerGold-=10
this.playerHealth+=400
})
Text("攻击+2").fontSize(26).width("80%").textAlign(TextAlign.Center)
.margin({top:20}).fontColor(Color.Blue)
.onClick(()=>{
if (this.playerGold<10){
return
}
this.playerGold-=10
this.playerAttack+=2
})
Text("防御+4").fontSize(26).width("80%").textAlign(TextAlign.Center)
.margin({top:20}).fontColor(Color.Blue)
.onClick(()=>{
if (this.playerGold<10){
return
}
this.playerGold-=10
this.playerDefense+=4
})
Text("返回").fontSize(26).width("80%").textAlign(TextAlign.Center)
.margin({top:40}).fontColor(Color.Blue)
.onClick(()=>{
this.isShopShow = Visibility.Hidden
})
}
.width("80%")
.height("70%")
.backgroundColor(Color.Gray)
.opacity(0.9)
.visibility(this.isShopShow)
数据持久化
preferences
工具类
创建工具类
private pref: preferences.Preferences
//获取Preferences 实例
createPreferences(context: Context) {
preferences.getPreferences(context, 'myPreference')
.then((object) => {
this.pref = object
console.log("创建Preference 实例成功")
})
.catch((error) => {
console.log("创建Preference 实例失败")
})
}
写入Preference 实例
//写入Preference 实例
async writePreferenceValue(key: string, value: preferences.ValueType) {
if (!this.pref) {
return
}
try {
await this.pref.put(key, value)
await this.pref.flush()
} catch (e) {
console.log("数据写入失败")
}
}
读取数据
//读取数据
async readPreferenceValue<T extends preferences.ValueType>(key: string, defaultValue: preferences.ValueType) {
let value0 :preferences.ValueType
if (!this.pref) {
return
}
try {
let value = await this.pref.get(key, defaultValue) as T
value0 = value
} catch (e) {
console.log(e)
}
return value0
}
在EntryAbility
页面的onCreate
方法传入参数
onCreate(want, launchParam) {
preferenceUtil.createPreferences(this.context)
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
}
在存档页面使用preferences
工具类进行数据初始化
@State SaveList: Array<number> = [0, 1, 2, 3, 4, 5]
StoredList: Array<number[]> = []
aboutToAppear() {
this.setStoredList()
}
//异步初始化
async setStoredList() {
let promises = [];
for (let i = 0; i < 6; i++) {
promises.push(this.getStoredDate(i.toString()));
}
this.StoredList = await Promise.all(promises);
}
async getStoredDate(number: string) {
let stores = await preferenceUtil.readPreferenceValue(number, 0) as number[];
return stores;
}
存储页面构建
Column() {
Text("保存列表").fontSize(26).height(40)
List() {
ForEach(this.SaveList, (list: number) => {
ListItem() {
Row() {
SaveView({ index: list, Stored: this.StoredList[list] })
}
.width("100%")
.height(130)
.margin({ top: 10 })
.backgroundColor(Color.Gray)
.opacity(0.8)
.onClick(() => {
})
}
})
}.width("100%").height("90%")
}.width("100%").height("100%")
自定义组件构建
index:number
PlayerHealth:number
Stored:number[]
//......
Row(){
Text("存档"+(this.index+1)).fontSize(26).width("30%")
Column(){
Row(){
Text("生命").width("25%")
Text("攻击").width("25%")
Text("防御").width("25%")
Text("金币").width("25%")
}.width("70%").justifyContent(FlexAlign.SpaceEvenly)
Row(){
if (this.Stored){
Text(this.Stored[0].toString()).width("25%")
Text(this.Stored[1].toString()).width("25%")
Text(this.Stored[2].toString()).width("25%")
Text(this.Stored[3].toString()).width("25%")
}
}.width("70%").height("90%")
.justifyContent(FlexAlign.SpaceEvenly)
}.justifyContent(FlexAlign.SpaceBetween)
}.width("100%").height(130)
数据跳转
保存跳转
stored :number[]
//....
saveRouter(){
let storage:number[] = [this.playerHealth,this.playerAttack, this.playerDefense,this.playerGold,
this.monsterId,this.monsterHealth]
router.pushUrl({
url:"pages/SavePage",
params:{
storage:storage,
source:"save"
}
})
}
//......
Button("保存").backgroundColor("")
.onClick(()=>{
this.saveRouter()
})
数据接收
stored: number[]
source: "save" | "load"
onPageShow() {
let params = router.getParams()
this.stored = params['storage'] as number[]
this.source = params["source"]
}
数据保存
//保存数据
async saveStoredDate(i: string) {
await preferenceUtil.writePreferenceValue(i, this.stored)
router.back({
url: "pages/GamePage",
params: {
stored: this.stored
}
})
}
页面回调
onPageShow() {
let params = router.getParams()
if (!params) {
this.playerHealth = player.PlayerHealth
this.playerAttack = player.PlayerAttack
this.playerDefense = player.PlayerDefense
this.playerGold = player.PlayerGold
this.monsterHealth = dataMonster.MonsterList[this.monsterId].Health
return
}
// this.stored= params['storage'] as number[]
this.playerHealth = params['stored'][0]
this.playerAttack = params['stored'][1]
this.playerDefense = params['stored'][2]
this.playerGold = params['stored'][3]
this.monsterId = params['stored'][4]
this.monsterHealth = params['stored'][5]
}
调用方法
.onClick(() => {
if (this.source === "save") {
//保存数据
this.saveStoredDate(list.toString())
}
})
读取跳转
//读取跳转
loadRouter(){
router.replaceUrl({
url:"pages/SavePage",
params:{
source:"load"
}
})
}
加载数据
//加载数据
loadStoredDate(i: number) {
let stored0 = this.StoredList[i]
if (!stored0) {
return
}
router.replaceUrl({
url: "pages/GamePage",
params: {
stored: stored0
}
})
}
判断调用
.onClick(() => {
if (this.source === "save") {
//保存数据
this.saveStoredDate(list.toString())
} else {
//加载数据
this.loadStoredDate(list)
}
})