前言
这个赛狗日,是给个框架让自己写。感觉难度有点大,语言和控件不熟。读他的说明和程序结构读了两三遍。最后才明白要干啥。写是写出来了,但是我感觉还可以优化,优化在最后说明,线来看下效果
效果展示
步骤
熟读任务说明
写一个程序第一步就是要明白,它要干啥,他怎么实现。而不是一遍写一遍想,写写改改,这样麻烦不说而且还很捞,我们这种新手一般都没有哪个本事一写就过。这本书很好,给了程序的结构和逻辑,只要自己想方法实现 就行了。所以要先读清楚任务说明,以免越写越乱。
搭界面
书上给的网站应该换地址了,进去不是的,是他们公司官网。无奈网上找不到合适的图片。只有自己画了,用PPT简单画一个。
👇👇👇👇
然后就按照书上的界面搭建一个。👇👇👇👇
图片网上好找。拍好布局,设置好控件的属性就OK,不想调图片尺寸,我就用的 背景图片填充,然后背景图片设为自适应。
开始代码
按照书上的结构代码分为一下四个部分:
- Form:界面控件的逻辑控制。
- Greyhound:赛狗的对象,每个对象控制一条狗。
- Guy:赌徒对象,一个赌徒有一个对应的一个对象。
- Bet:赌局对象(个人理解),控制一个赌徒本局的下注情况。
From.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Day_at_the_Races
{
public partial class Form1 : Form
{
Greyhound[] greyhounds = new Greyhound[4];//声明一个数组管理Grehound对象
Guy[] guys = new Guy[3];//声明一个数组管理Guy对象
Random random = new Random();//创建一个Random对象。
public Form1()
{
InitializeComponent();
initDog();//初始化狗
initGuy();//初始化赌徒和对应的赌注。
}
/// <summary>
/// 初始化比赛的狗:绑定狗的图片对象,赛道的长度,起点位置,随机Random对象;同时,初始化狗的位置。
/// </summary>
public void initDog()
{
//创建对象,同时对属性赋值。
greyhounds[0] = new Greyhound()
{
MypictureBox = dog1,
StartingPosition = raceRoad.Left,
RacetrackLength = raceRoad.Width,
randomizer = random
};
//重置狗的位置
greyhounds[0].TakeStaringPosition();
//Console.WriteLine("0号狗位置"+ greyhounds[0].StartingPosition);
//Console.WriteLine("0号狗图片左边位置" + greyhounds[0].MypictureBox.Left);
greyhounds[1] = new Greyhound()
{
MypictureBox = dog2,
StartingPosition = raceRoad.Left,
RacetrackLength = raceRoad.Width,
randomizer = random
};
greyhounds[1].TakeStaringPosition();
//0Console.WriteLine("1号狗位置" + greyhounds[1].StartingPosition);
//Console.WriteLine("1号狗图片左边位置" + greyhounds[1].MypictureBox.Left);
greyhounds[2] = new Greyhound()
{
MypictureBox = dog3,
StartingPosition = raceRoad.Left,
RacetrackLength = raceRoad.Width,
randomizer = random
};
greyhounds[2].TakeStaringPosition();
//Console.WriteLine("2号狗位置" + greyhounds[2].StartingPosition);
//Console.WriteLine("2号狗图片左边位置" + greyhounds[2].MypictureBox.Left);
greyhounds[3] = new Greyhound()
{
MypictureBox = dog4,
StartingPosition = raceRoad.Left,
RacetrackLength = raceRoad.Width,
randomizer = random
};
greyhounds[3].TakeStaringPosition();
//Console.WriteLine("3号狗位置" + greyhounds[3].StartingPosition);
//Console.WriteLine("3号狗图片左边位置" + greyhounds[3].MypictureBox.Left);
}
/// <summary>
/// 初始化赌徒,姓名 ,赌资,radio button,和label;最后创建每个的赌注(bet)对象;
/// </summary>
public void initGuy()
{
guys[0] = new Guy()
{
Name = "卢本伟",
Cash = 50,
MyRadio = radiolbw,
Mylabel = lablbwInfo
};
guys[1] = new Guy()
{
Name = "孙笑川",
Cash = 60,
MyRadio = radsxc,
Mylabel = labsxcInfo
};
guys[2] = new Guy()
{
Name = "大司马",
Cash = 20,
MyRadio = raddsm,
Mylabel = labdsmInfo
};
initbet();
}
/// <summary>
/// 初始化bet对象,默认以0未初始化。
/// </summary>
public void initbet()
{
guys[0].UpdateLabel();
guys[0].PlaceBet(0, 0);
guys[1].UpdateLabel();
guys[1].PlaceBet(0, 0);
guys[2].UpdateLabel();
guys[2].PlaceBet(0, 0);
}
/// <summary>
/// timer调用的方法,每次调用都进行四条狗的Run函数,检测到有true返回就开始进行Guy的Collect(),结算。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void timer1_Tick(object sender, EventArgs e)
{
for (int i = 0; i < 4; i++)
{
if (greyhounds[i].Run())
{
timer1.Stop();
for (int j = 0; j < 3; j++)
{
guys[j].Collect(i);
}
//显示提示信息
MessageBox.Show("本局" + i + "号狗取得胜利!");
//释放每个狗的对象
for (int k = 0; k < 4; k++)
{
greyhounds[k] = null;
}
//初始化狗
initDog();
//初始化赌注,赌注对象已经在Collect()里面删除。
initbet();
//将面板组件激活,进行下次赌局。
groupBox1.Enabled = true;
//重置单选框
radiolbw.Checked = false;
radsxc.Checked = false;
raddsm.Checked = false;
money.Text = "0";//下注输入框重置
dog.Text = "0";
break;//一定要加上,结束循环,如果不加初始化后还会调用greyhounds[i].Run(),就会不在原点。
}
}
}
/// <summary>
/// 点击后进行最下方Label跟新和两个输入框的刷新。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void radiolbw_Click(object sender, EventArgs e)
{
labguy.Text = "卢本伟压注";
money.Text = "0";
dog.Text = "0";
}
private void raddsm_Click(object sender, EventArgs e)
{
labguy.Text = "大司马压注";
money.Text = "0";
dog.Text = "0";
}
private void radsxc_Click(object sender, EventArgs e)
{
labguy.Text = "孙笑川压注";
money.Text = "0";
dog.Text = "0";
}
/// <summary>
/// 比赛开始按钮,激活计时器。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_start_Click(object sender, EventArgs e)
{
//验证本局是否有人下注
if (guys[0].Mybet.Amount == 0 && guys[1].Mybet.Amount == 0 && guys[2].Mybet.Amount == 0)
{
MessageBox.Show("暂无选手下注!");
}
else
{
groupBox1.Enabled = false;//禁用下注面板
timer1.Interval = 100;//设置计时器的时间间隔为100ms。
timer1.Start();//开始计时器
}
}
/// <summary>
/// 确认下注按钮,跟新Guy的bet对象,跟新label
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_ok_Click(object sender, EventArgs e)
{
//如果此时选中的是卢本伟的单选按钮。
if (radiolbw.Checked)
{
if (Int32.Parse(money.Text) < 5)
{
MessageBox.Show("下注金额至少为5元!");
}
else if (Int32.Parse(dog.Text) < 0 || Int32.Parse(dog.Text) > 3)
{
MessageBox.Show("没有" + Int32.Parse(dog.Text) + "号狗!");
}
else
{
guys[0].PlaceBet(Int32.Parse(money.Text), Int32.Parse(dog.Text));//更新bet对象
guys[0].UpdateLabel();//更新label
}
}
//如果此时选中的是孙笑川的单选按钮。
if (radsxc.Checked)
{
if (Int32.Parse(money.Text) < 5)
{
MessageBox.Show("下注金额至少为5元!");
}
else if (Int32.Parse(dog.Text) < 0 || Int32.Parse(dog.Text) > 3)
{
MessageBox.Show("没有" + Int32.Parse(dog.Text) + "号狗!");
}
else
{
guys[1].PlaceBet(Int32.Parse(money.Text), Int32.Parse(dog.Text));
guys[1].UpdateLabel();
}
}
//如果此时选中的是大司马的单选按钮。
if (raddsm.Checked)
{
if (Int32.Parse(money.Text) < 5)
{
MessageBox.Show("下注金额至少为5元!");
}
else if (Int32.Parse(dog.Text) < 0 || Int32.Parse(dog.Text) > 3)
{
MessageBox.Show("没有" + Int32.Parse(dog.Text) + "号狗!");
}
else
{
guys[2].PlaceBet(Int32.Parse(money.Text), Int32.Parse(dog.Text));
guys[2].UpdateLabel();
}
}
}
}
}
Greyhound.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Day_at_the_Races
{
public class Greyhound
{
public int StartingPosition;//起点位置,我设置的是以赛道的左边为起点
public int RacetrackLength;//赛道长度,就是赛道图片的with宽度,由于我算终点的宽度,我量的是100个像素,所以计算的时候要减去95(营造冲线假象)
public PictureBox MypictureBox = null;//狗的图片
public int Location = 0;//狗跑出的距离,再次距离上进行位置叠加
public Random randomizer;
public bool Run()
{
bool isWin = false;//输赢的标志变量
Location += randomizer.Next(0,20);//随机对象,随机移动0-20个像素。
MypictureBox.Left = StartingPosition + Location;//位置更新
if(MypictureBox.Left>= (RacetrackLength-95) - MypictureBox.Width)//判断是否冲线,95在上方属性有解释。
{
isWin = true;
}
return isWin;
}
//初始狗的位置
public void TakeStaringPosition()
{
this.MypictureBox.Left = StartingPosition;
this.Location = 0;
}
}
}
Guy.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Day_at_the_Races
{
public class Guy
{
public string Name;//姓名
public Bet Mybet;//赌注对象
public int Cash;//赌资
public RadioButton MyRadio;//radio button对象
public Label Mylabel;//label对象
//跟新radio button对象
public void UpdateLabel()
{
MyRadio.Text = Name + "有" + Cash + "元";
}
//清除赌局对象
public void ClearBet()
{
Mybet = null;
}
//创建赌局对象
public bool PlaceBet(int BetAmount,int DogTOWin)
{
if (Mybet != null)//为空就初始化
{
//if (BetAmount < 5)
//{
// MessageBox.Show("下注金额至少为5元!"); //已经在开始按钮处判断
// return false;
//}
if(BetAmount > Cash){
MessageBox.Show(Name+"的金额不足"+BetAmount+"!");
return false;
}
else
{
//进行行赌局对象属性的更新
Console.WriteLine("下注成功!");
Mybet.Amount = BetAmount;
Mybet.dog = DogTOWin;
Mylabel.Text = Mybet.GetDiscreaption();
return true;
}
}
else
{
//初始化
Mybet = new Bet() { Amount = BetAmount, Better = this, dog = DogTOWin };
Mylabel.Text = Mybet.GetDiscreaption();
return false;
}
}
//进行赌局结算,更新资产,radio button的文字,删除上局的bet对象。
public void Collect(int Winner)
{
Cash += Mybet.Payout(Winner);
ClearBet();
MyRadio.Text= Name + "有" + Cash + "元";
}
}
}
Bet.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Day_at_the_Races
{
public class Bet
{
public int Amount;//下注金额
public int dog;//压得狗
public Guy Better;//赌徒
//赌徒在本局一个描述
public string GetDiscreaption()
{
string dis = "";
if (Amount == 0)
{
dis = Better.Name + "没有下注";
}
else
{
dis = Better.Name + "下注" + Amount + "元,赌" + dog + "号狗会赢";
}
return dis;
}
//返回本局的输赢金额
public int Payout(int Winner)
{
if (dog == Winner)
{
return Amount;
}
else
{
return -Amount;
}
}
}
}
写在后面的话
本次遇见的一些问题
论坛给了这么好的交流平台,当然要交流下自己的问题。作为初学者更应该交流。
- 书要仔细,最开始遗漏了一个bet按钮,书上也不显眼,纠结了好久Raadiobutton功能写不好,总不满意。最后一看下注那句话里面藏个按钮。。。这样就好办了。
- 最后判断完胜利者,重新初始化狗的位置时总有偏移。👇👇
然后我输出图片的位置查看一下初始化时图片的位置发现没毛病。👇
然后我以为是电脑有问题了,直到一次0号狗跑了第一和一次3号狗跑了第一,一对比才想到,没有控制好结束的逻辑,结束后不适用break初始化后还会调用后面狗的Run(),从而发生偏移。
关于优化
可以设定狗的特性
可以参照游戏设定狗的一些特性,让比赛随机,但又可以预料。
去掉Bet类
我觉得哪个Bet类显得有点碍事,让程序有点复杂。可以直接合并到Guy类里面。这个只是个人见解,如不正确还望大神指点。
自己的不足
我写完后自己有看了遍代码,虽然完成了书上的功能,但是觉得自己也有写偏的。比如:
- Bet类默认都创建,我觉得书上得意思是,这局赌了才会创建一个Bet对象,而我是默认都创建了。同时还增加了多余的判断条件。
- 关于label的跟新我都是依赖Bet类里面的GetDiscreaption()来获得文本,总体我觉得不好。
可能还有许多不足的地方,希望有大佬看出来能够交流下,我以后避免再犯这样的错误。❤❤❤🙇♂️
2020-2-24