溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

基于WPF如何實(shí)現(xiàn)經(jīng)典紙牌游戲

發(fā)布時(shí)間:2023-02-27 09:30:43 來源:億速云 閱讀:208 作者:iii 欄目:開發(fā)技術(shù)

這篇“基于WPF如何實(shí)現(xiàn)經(jīng)典紙牌游戲”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“基于WPF如何實(shí)現(xiàn)經(jīng)典紙牌游戲”文章吧。

1 紙牌類

之所以產(chǎn)生這個(gè)無聊至極的念頭,是因?yàn)榘l(fā)現(xiàn)Unicode中竟然有這種字符。

基于WPF如何實(shí)現(xiàn)經(jīng)典紙牌游戲

這就意味著不用任何資源就可以實(shí)現(xiàn)一些紙牌游戲,效果如下圖所示

基于WPF如何實(shí)現(xiàn)經(jīng)典紙牌游戲

這就意味著不用任何資源就可以實(shí)現(xiàn)一些紙牌游戲,效果如下圖所示

#region 常量
private static readonly Dictionary<string, string[]> CardNames = new Dictionary<string, string[]>{
{"Spade", new string[13]{"????","????","????","????","????","????","????","????","????","????","????","????","????" } },
{"Heart", new string[13]{"????", "????", "????", "????", "????", "????", "????", "????", "????", "????", "????", "????", "????" } },
{"Diamond", new string[13]{"????", "????", "????", "????", "????", "????", "????", "????", "????", "????", "????", "????", "????" } },
{"Club" , new string[13]{"????", "????", "????", "????", "????", "????", "????", "????", "????", "????", "????", "????", "????"}}
};
#endregion

#region 卡牌類型
private class Card
{
	public Card(string name, int number, string type, bool red, int index)
	{
		Name =name;
		Number = number;
		Type = type;
		Red = red;
		Index = index;
	}

	public int Index;
	public string Name;
	public int Number;
	public bool Red;
	public string Type;
	public int Region;
}
#endregion

在實(shí)現(xiàn)了紙牌類之后,將每個(gè)紙牌放到一個(gè)Button的Tag中,然后再 為Button添加各種事件,就能實(shí)現(xiàn)這個(gè)游戲了。

2 布局

由于是動(dòng)態(tài)布局,所以建議使用Canvas,xaml界面十分簡潔,除了一個(gè)刷新按鈕,剩下的就只有畫布了。

    <StackPanel>
        <ToolBar DockPanel.Dock="Top" Margin="0 0 0 20">
            <Button Content="????" Click="btnUpdate_Click"/>
        </ToolBar>
        <Canvas x:Name="cvMain" Height="400"/>

    </StackPanel>

經(jīng)典紙牌游戲大致可以分為12個(gè)區(qū)域,如圖所示

基于WPF如何實(shí)現(xiàn)經(jīng)典紙牌游戲

這些個(gè)區(qū)域就可決定紙牌的位置,所以需要一個(gè)用來存放區(qū)間信息的變量

private List<int>[] cardIndex;

cardIndex是由12個(gè)List<int>組成的數(shù)組,然后每個(gè)Button的位置用下面的方式來設(shè)定

private void setBtnPosition(Button btn, int region)
{
    Canvas.SetLeft(btn, region % 6 * dw);
    Canvas.SetTop(btn, region / 6 * dh);
    Canvas.SetZIndex(btn, cardIndex[region].Count);
}

其中,SetLeft即控件據(jù)Canvas左端的距離,可以理解為x坐標(biāo);dw,dh為全局變量,用來存放每個(gè)區(qū)間的尺寸。SetTop對(duì)應(yīng)的為y坐標(biāo)。SetZIndex表示層級(jí)關(guān)系,值越大則越在上面。

3 初始化

初始化需要一個(gè)隨機(jī)數(shù)組,目的是將牌打散。這里用了一個(gè)非常Low的方案,即生成隨機(jī)數(shù),然后交換自然序列中兩個(gè)隨機(jī)數(shù)所在位置的值。

private int[] RandomArray(int length)
{
    int[] arr = new int[length];
    for (int i = 0; i < length; i++)
        arr[i] = i;
    int times = rand.Next(10, 100);
    for (int i = 0; i < times; i++)
    {
        int a = rand.Next(0, length - 1);
        int b = rand.Next(0, length - 1);
        var temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
    return arr;
}

接下來就是初始化代碼,這里按照平時(shí)發(fā)牌的順序,先生成這個(gè)區(qū)域的紙牌

基于WPF如何實(shí)現(xiàn)經(jīng)典紙牌游戲

然后再生成牌堆。

public void InitCards()
{
    cvMain.Children.Clear();
    cards = new List<Card>();
    cardIndex = new List<int>[12];          //所有的撲克被劃分為12個(gè)區(qū)域
    for (int i = 0; i < 12; i++)
        cardIndex[i] = new List<int>();

    int index = 0;
    foreach (var key in CardNames.Keys)
        for (int i = 0; i < 13; i++)
            cards.Add(new Card(CardNames[key][i], i, key,
                key == "Heart" || key == "Diamond", index++));
    
    var orders = RandomArray(52);

    index = 0;
    for (int i = 0; i < 6; i++)
        for (int j = i; j < 6; j++)
        {
            var card = cards[orders[index++]];
            cardIndex[6 + j].Add(cvMain.Children.Count);
            var btn = setOneButton(card);
            if (i == j)
            {
                coverCard(btn, false);      //當(dāng)i==j時(shí)翻面
                SetOneColumn(i);
            }
            card.Region = 6 + j;
            setBtnPosition(btn, card.Region);
        }

    while (index<52)
    {
        var card = cards[orders[index++]];
        cardIndex[0].Add(cvMain.Children.Count);
        var btn = setOneButton(card);
        card.Region = 0;
        setBtnPosition(btn, 0);
        btn.Click += Card_Click;
    }
}

其中,SetOneColumn用于下面牌的上下排序,定義為

private void SetOneColumn(int region)
{
    var count = cardIndex[region].Count;
    var left = (region - 6) * dw;
    var top0 = dh;
    int i = 0;
    var ddh = (0.8 + 2.5 * count / 15) * dh / count;
    foreach (var index in cardIndex[region])
    {
        var btn = cvMain.Children[index];
        Canvas.SetLeft(btn, left);
        Canvas.SetTop(btn, top0 + ddh * (i++));
        Canvas.SetZIndex(btn, i);
    }
}

4 事件

針對(duì)紙牌游戲來說,鼠標(biāo)事件可分為兩類,一是點(diǎn)擊牌堆需要發(fā)牌;二是拖動(dòng)其他位置的牌。

點(diǎn)擊牌堆

點(diǎn)擊牌堆需要注意,當(dāng)牌堆中的牌沒有了之后,需要將1區(qū)的牌還給牌堆。

private void Card_Click(object sender, RoutedEventArgs e)
{
    var btn = sender as Button;
    var card = btn.Tag as Card;
    if (card.Region > 0)
        return;

    var count = cardIndex[0].Count;
    var num = Math.Min(count, numCard);
    for (int _ = 0; _ < num; _++)
    {
        var index = cardIndex[0][count - num];      //canvas中的順序
        cardIndex[0].Remove(index);
        cardIndex[1].Add(index);

        btn = cvMain.Children[index] as Button;
        btn.Click -= Card_Click;
        btn.PreviewMouseLeftButtonDown += Card_PreviewLeftDown;
        coverCard(btn, false);
        setBtnPosition(btn, 1);
        card = btn.Tag as Card;
        card.Region = 1;
    }

    if (cardIndex[0].Count > 0 || cardIndex[1].Count <= numCard)
        return;

    foreach (var index in cardIndex[1])
    {
        cardIndex[0].Add(index);
        btn = cvMain.Children[index] as Button;
        btn.Click += Card_Click;
        btn.PreviewMouseLeftButtonDown -= Card_PreviewLeftDown;
        coverCard(btn, true);
        setBtnPosition(btn, 0);
        card = btn.Tag as Card;
        card.Region = 0;
    }
    cardIndex[1] = new List<int>();
}

拖動(dòng)

拖動(dòng)主要包含三個(gè)動(dòng)作,即鼠標(biāo)按下、鼠標(biāo)挪動(dòng)、鼠標(biāo)彈開,所以對(duì)應(yīng)三個(gè)函數(shù),且當(dāng)鼠標(biāo)按下之后,才掛載鼠標(biāo)挪動(dòng)的事件。而鼠標(biāo)彈起之后,則判斷我們拖動(dòng)的牌的最終位置。

private void Card_PreviewLeftDown(object sender, MouseButtonEventArgs e)
{
    btnNow = sender as Button;
    if (btnNow.Content.ToString() == bgCard)
        return;
    var card = btnNow.Tag as Card;
    regionNow = cardIndex[card.Region];
    indexNow = regionNow.IndexOf(
        cvMain.Children.IndexOf(btnNow));

    var count = regionNow.Count;
    offsets = new List<Point>();
    for (int i = indexNow; i < count; i++)
    {
        var btn = cvMain.Children[regionNow[i]] as Button;
        offsets.Add(Mouse.GetPosition(btn));
        Canvas.SetZIndex(btnNow, 100 + i);
    }

    btnNow.PreviewMouseLeftButtonUp += Card_PreviewLeftUp;
    btnNow.PreviewMouseMove += Card_PreviewMouseMove;
}

private void Card_PreviewMouseMove(object sender, MouseEventArgs e)
{
    var p = Mouse.GetPosition(cvMain);
    for (int i = indexNow; i < regionNow.Count; i++)
    {
        var btn = cvMain.Children[regionNow[i]] as Button;
        Canvas.SetLeft(btn, p.X - offsets[i - indexNow].X);
        Canvas.SetTop(btn, p.Y - offsets[i - indexNow].Y);
    }
}

private void Card_PreviewLeftUp(object sender, MouseButtonEventArgs e)
{

    btnNow.PreviewMouseLeftButtonUp -= Card_PreviewLeftUp;
    btnNow.PreviewMouseMove -= Card_PreviewMouseMove;

    var p = Mouse.GetPosition(cvMain);
    int region = (int)(p.X / dw) + (p.Y > dh ? 6 : 0);
    var index = cardIndex[region].Count - 1;        //目標(biāo)區(qū)域最上面的牌的序號(hào)
    var card = btnNow.Tag as Card;
    int srcRegion = card.Region;

    //牌在挪動(dòng)之后有兩種可能,一種是成功了,另一種是失敗了
    bool suc = region != srcRegion;     //如果挪動(dòng)的區(qū)域相同,則必失敗
    bool subSuc;
    suc &= region > 1;              //如果向牌堆挪動(dòng),則必失敗。
    if (index < 0)
    {//A和K的情況滿足任何一種即可成功
        subSuc = region > 1 && region < 6 && card.Number == 0;
        subSuc |= region > 5 && card.Number == 12;
    }
    else
    {
        var tarBtn = cvMain.Children[cardIndex[region][index]] as Button;
        var tarCard = tarBtn.Tag as Card;
        var flag = tarCard.Type == card.Type;
        var minus = card.Number - tarCard.Number;
        subSuc = region > 1 && region < 6 && flag && (minus == 1);
        subSuc |= region > 5 && (!flag) && (minus == -1);
    }
    reGroup(suc & subSuc, card, srcRegion, region);
}

牌的去留

通過reGroup函數(shù)決定牌最終的狀態(tài)。

private void reGroup(bool suc, Card card, int srcRegion, int tarRegion)
{
    if (suc)
    {
        if (tarRegion > 5)
            setNewRegion(srcRegion, tarRegion);
        else
            setNewRegion(btnNow, card, srcRegion, tarRegion);
    }
    if (srcRegion < 6)
        setBtnPosition(btnNow, srcRegion);
    else
    {
        SetOneColumn(srcRegion);
        var i = regionNow.Count;
        if (i > 0)
        {
            var btn = cvMain.Children[regionNow[i - 1]] as Button;
            coverCard(btn, false);
        }
    }

}
private void setNewRegion(Button btn, Card card, int src, int tar)
{
    int i = cardIndex[src].Count - 1;
    cardIndex[tar].Add(cardIndex[src][i]);
    cardIndex[src].RemoveAt(i);
    card.Region = tar;
    setBtnPosition(btn, tar);
}

//src和tar均為大區(qū)
private void setNewRegion(int src, int tar)
{
    var count = regionNow.Count;
    for (int i = indexNow; i < count; i++)
    {
        var btn = cvMain.Children[regionNow[indexNow]] as Button;
        cardIndex[tar].Add(regionNow[indexNow]);
        regionNow.RemoveAt(indexNow);
        var card = btn.Tag as Card;
        card.Region = tar;
        Canvas.SetZIndex(btn, cardIndex[tar].Count);
    }
    SetOneColumn(tar);
    if (regionNow.Count > 0)
    {
        var btn = cvMain.Children[regionNow[indexNow - 1]] as Button;
        coverCard(btn, false);
    }

}

以上就是關(guān)于“基于WPF如何實(shí)現(xiàn)經(jīng)典紙牌游戲”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對(duì)大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

wpf
AI