使用C#写一个QQ群版像素画游戏

昨天苦命鸳鸯节闲得无聊,就想着能不能抄袭一下之前那个像素画的游戏,经过一下午+一晚上的研究,在昨晚终于把这个小游戏的功能集成到了我的qq机器人里面,效果如下:

这个小游戏的代码实现了以下几个基本功能:

  1. 可以通过指令修改置顶区域的颜色

  2. 颜色完全可以自定义

  3. 判断用户是否是过了五分钟后才再次进行颜色修改的,否则拒绝修改

  4. 每次修改保存一张备份图片

机器人使用了酷Q pro版(air免费版无法发送图片),和C#sdk(https://cqp.cc/t/29261

首先,因为是要修改图片,所以我们需要先在C#工程内引入“System.Drawing

引用完毕后,在开头加上:

using System.Drawing;

修改图片某一个像素点颜色的方法是“SetPixel”,所以构造方法如下:

        public static Bitmap SetPels(Bitmap Pict, Color color, int x, int y, int w, int h)
        {
            //遍历矩形框内的各象素点
            for (int i = x; i < x + w; i++)
            {
                for (int j = y; j < y + h; j++)
                {
                    Pict.SetPixel(i, j, color);//设置当前象素点的颜色
                }
            }
            return Pict;
        }

这样就可以改变指定区域的颜色了,但是qq内发的图片不能精确到指定坐标,毕竟就只是一张图片,所以我把像素点给扩大到了17*17的大小,并且增加了新的方法:

        //x,y限制0-29
        private static Bitmap SetPoint(Bitmap Pict, Color color, int x, int y)
        {
            return SetPels(Pict, color, x * 17, y * 17, 17, 17);
        }

这样就可以方便地修改指定区域了。

我们再加上一个打开文件的方法:

        /// <summary>
        /// 通过FileStream 来打开文件,这样就可以实现不锁定Image文件,到时可以让多用户同时访问Image文件
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static Bitmap ReadImageFile(string path)
        {
            FileStream fs = File.OpenRead(path); //OpenRead
            int filelength = 0;
            filelength = (int)fs.Length; //获得文件长度 
            Byte[] image = new Byte[filelength]; //建立一个字节数组 
            fs.Read(image, 0, filelength); //按字节流读取 
            System.Drawing.Image result = System.Drawing.Image.FromStream(fs);
            fs.Close();
            Bitmap bit = new Bitmap(result);
            return bit;
        }

有了这些方法,我们就可以进行测试了(提前准备好图片文件):

string picPath = @"C:\Users\Administrator\Desktop\kuqpro\data\image\pixel_game\1.bmp";
Bitmap pic = ReadImageFile(picPath);
pic = SetPoint(pic, color.red, 1, 1);
pic.Save(picPath);

如果不出意外的话,会在左上方出现一个红色方框,而且不是紧贴着边缘的。

我们接下来处理用户时间的问题,首先先使用xml保存配置文件,我写了以下几个方法,效率可能会很低,建议大家使用其他的方法进行替换:

static string path = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
public static string xml_get(long group, string msg)
{
    dircheck(group);
    XElement root = XElement.Load(path + group + ".xml");
    string ansall = "";
    foreach (XElement mm in root.Elements("msginfo"))
    {
        if (msg == mm.Element("msg").Value)
        {
            ansall = mm.Element("ans").Value;
            break;
        }
    }
    return ansall;
}
public static void del(long group, string msg)
{
    dircheck(group);
    string gg = group.ToString();
    XElement root = XElement.Load(path + group + ".xml");
    var element = from ee in root.Elements()
                  where (string)ee.Element("msg") == msg
                  select ee;
    if (element.Count() > 0)
    {
        element.First().Remove();
    }
    root.Save(path + group + ".xml");
}
public static void insert(long group, string msg, string ans)
{
    if(msg.IndexOf("\r\n") < 0 & msg != "")
    {
        dircheck(group);
        XElement root = XElement.Load(path + group + ".xml");
        XElement read = root.Element("msginfo");
        read.AddBeforeSelf(new XElement("msginfo",
               //new XElement("group", group),
               new XElement("msg", msg),
               new XElement("ans", ans)
               ));
        root.Save(path + group + ".xml");
    }
}
public static void createxml(long group)
{
    XElement root = new XElement("Categories",
        new XElement("msginfo",
            //new XElement("group", 123),
            new XElement("msg", "初始问题"),
            new XElement("ans", "初始回答")
            )
       );
    root.Save(path + group + ".xml");
}
public static void dircheck(long group)
{
    if (File.Exists(path + group + ".xml"))
    {
        //MessageBox.Show("存在文件");
        //File.Delete(dddd);//删除该文件
    }
    else
    {
        //MessageBox.Show("不存在文件");
        createxml(group);//创建该文件,如果路径文件夹不存在,则报错。
    }
}

另外,因为我们需要统计时间差,所以我使用了timestamp来记录时间(不会用datetime?,求大佬讲解)

        /// <summary>  
        /// DateTime时间格式转换为Unix时间戳格式  
        /// </summary>  
        /// <param name="time"> DateTime时间格式</param>  
        /// <returns>Unix时间戳格式</returns>  
        public static int ConvertDateTimeInt(System.DateTime time)
        {
            System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
            return (int)(time - startTime).TotalSeconds;
        }

于是,整段响应群消息的代码如下:

/// <summary>
/// Type=2 群消息。
/// </summary>
/// <param name="subType">子类型,目前固定为1。</param>
/// <param name="sendTime">发送时间(时间戳)。</param>
/// <param name="fromGroup">来源群号。</param>
/// <param name="fromQQ">来源QQ。</param>
/// <param name="fromAnonymous">来源匿名者。</param>
/// <param name="msg">消息内容。</param>
/// <param name="font">字体。</param>
public override void GroupMessage(int subType, int sendTime, long fromGroup, long fromQQ, string fromAnonymous, string msg, int font)
{
    if (fromQQ == 80000000)
        return;
    // 处理群消息。
...一堆代码
    else if(msg.IndexOf("pixel") == 0 && (msg.Length - msg.Replace("/","").Length) == 2)
    {
        string fromqqtime_get = xml_get(20, fromQQ.ToString());
        int fromqqtime = 0;
        try
        {
            fromqqtime = int.Parse(fromqqtime_get);
        }
        catch { }
        if(ConvertDateTimeInt(DateTime.Now)-fromqqtime < 300)
        {
            SendMinecraftMessage(fromGroup, CQ.CQCode_At(fromQQ) + "你需要再等" + (300 - ConvertDateTimeInt(DateTime.Now) + fromqqtime) + "秒才能继续放像素点");
            return;
        }
        string get_msg = msg.Replace("pixel", ""), getx = "", gety = "", getcolor = "";
        int placex, placey;
        string[] str2;
        int count_temp = 0;
        str2 = get_msg.Split('/');
        foreach (string i in str2)
        {
            if (count_temp == 0)
            {
                getx = i.ToString();
                count_temp++;
            }
            else if (count_temp == 1)
            {
                gety = i.ToString();
                count_temp++;
            }
            else if (count_temp == 2)
            {
                getcolor = i.ToString();
                count_temp++;
            }
        }
        try
        {
            placex = int.Parse(getx) - 1;
            placey = int.Parse(gety) - 1;
            if (getcolor.IndexOf("#") == -1 || placex > 29 || placey > 29)
                throw new ArgumentNullException("fuck wrong color");
        }
        catch
        {
            SendMinecraftMessage(fromGroup, "放置像素点时遇到未知错误,请检查颜色与坐标是否正确");
            return;
        }
        int picCount;
        try
        {
            picCount = int.Parse(xml_get(20, "count"));
        }
        catch
        {
            SendMinecraftMessage(fromGroup, "遇到致命性错误,请联系晨旭修复");
            return;
        }
        try
        {
            string picPath = @"C:\Users\Administrator\Desktop\kuqpro\data\image\pixel_game\" + picCount + ".bmp";
            Bitmap pic = ReadImageFile(picPath);
            pic = SetPoint(pic, ColorTranslator.FromHtml(getcolor), placex, placey);
            picCount++;
            picPath = @"C:\Users\Administrator\Desktop\kuqpro\data\image\pixel_game\" + picCount + ".bmp";
            pic.Save(picPath);
        }
        catch
        {
            SendMinecraftMessage(fromGroup, "遭遇未知错误");
            return;
        }
        del(20, "count");
        del(20, fromQQ.ToString());
        insert(20, "count", picCount.ToString());
        insert(20, fromQQ.ToString(), ConvertDateTimeInt(DateTime.Now).ToString());
        SendMinecraftMessage(fromGroup, "[CQ:image,file=pixel_game\\" + picCount + ".bmp]\r\n图片修改完成!" + DateTime.Now.ToString() + CQ.CQCode_At(fromQQ));
    }
...一堆代码
}

如有问题,可以在下面留言。

代码写的比较啰嗦,大佬们请耐心地看?

发表评论

您的电子邮箱地址不会被公开。