用C#实现的黑客帝国中的字符雨特效

时间:2024-02-25 10:57:57

在做一个WinForm登录框时,突然想到,如果有黑客帝国中字符雨的特效做背景,那应该蛮Cool的,所以就有了如下代码,随意写的,有点乱。

 

    public partial class CharacterRain : Component
    
{
        
/// <summary>
        
/// 必需的设计器变量。
        
/// </summary>

        private System.ComponentModel.IContainer components = null;

        
/// <summary> 
        
/// 清理所有正在使用的资源。
        
/// </summary>
        
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>

        protected override void Dispose(bool disposing)
        
{
            
if (RainThread != null && RainThread.IsAlive)
                Stop();

            
if (disposing && (components != null))
            
{
                components.Dispose();
            }

            
base.Dispose(disposing);
        }


        
组件设计器生成的代码

        
public CharacterRain()
        
{
            Initialize();

            InitializeComponent();
        }


        
public CharacterRain(IContainer container)
        
{
            Initialize();

            container.Add(
this);

            InitializeComponent();
        }


        
private object BMPLock = new object();
        
private Bitmap BMP;
        
private Graphics Graph;

        
private void Initialize()
        
{
            
this.DrawRainEvent = new EventHandler(CharacterRainPanel_DrawRainEvent);
            OnPaint 
= new PaintEventHandler(ShowWindow_Paint);
        }


        
private PaintEventHandler OnPaint;

        
private char[] CHARACTERS = \'`\'\'1\'\'2\'\'3\'\'4\'\'5\'\'6\'\'7\'\'8\'\'9\'\'0\'\'-\'\'=\'\'\\\'\'q\'\'w\'\'e\'\'r\'\'t\'\'y\'\'u\'\'i\'\'o\'\'p\'\'[\'\']\'\'a\'\'s\'\'d\'\'f\'\'g\'\'h\'\'j\'\'k\'\'l\'\';\'\'\\'\'\'z\'\'x\'\'c\'\'v\'\'b\'\'n\'\'m\'\',\'\'.\'\'/\'\'~\'\'!\'\'@\'\'#\'\'$\'\'%\'\'^\'\'&\'\'*\'\'(\'\')\'\'_\'\'+\'\'|\'\'Q\'\'W\'\'E\'\'R\'\'T\'\'Y\'\'U\'\'I\'\'O\'\'P\'\'{\'\'}\'\'A\'\'S\'\'D\'\'F\'\'G\'\'H\'\'J\'\'K\'\'L\'\':\'\'"\', \'Z\', \'X\', \'C\', \'V\', \'B\', \'N\', \'M\', \'<\', \'>\', \'?\' };
        private static readonly Random Rand = new Random();

        
private Control _ShowWindow = null;
        
/// <summary>
        
/// 获取、设置用于显示字符雨的窗口(或控件)
        
/// </summary>

        public Control ShowWindow
        
{
            
get return _ShowWindow; }
            
set
            
{
                
if (_ShowWindow != null)
                    _ShowWindow.Paint 
-= OnPaint;

                _ShowWindow 
= value;

                
if (_ShowWindow != null)
                    _ShowWindow.Paint 
+= OnPaint;
            }

        }


        
void ShowWindow_Paint(object sender, PaintEventArgs e)
        
{
            
//Paint();
            if (BMP != null)
            
{
                
lock (BMPLock)
                
{
                    
//e.Graphics.Clear(_ShowWindow.BackColor);
                    try
                    
{
                        e.Graphics.DrawImage(BMP, 
00);
                    }

                    
catch
                    
{
                    }

                }

            }

        }


        
private string _RainCharacters = "`1234567890-=\\qwertyuiop[]asdfghjkl;\'zxcvbnm,./~!@#$%^&*()_+|QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>?";
        /// <summary>
        
/// 获取、设置字符雨中可能出现的字符
        
/// </summary>

        public string RainCharacters
        
{
            
get return _RainCharacters; }
            
set
            
{
                
if (string.IsNullOrEmpty(value))
                    value 
= "`1234567890-=\\qwertyuiop[]asdfghjkl;\'zxcvbnm,./~!@#$%^&*()_+|QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>?";
                _RainCharacters = value;
                CHARACTERS 
= value.ToCharArray();
            }

        }


        
private Color _RainHeadColor = Color.Lime;
        
/// <summary>
        
/// 获取、设置字符雨头的颜色
        
/// </summary>

        public Color RainHeadColor
        
{
            
get return _RainHeadColor; }
            
set
            
{
                
if (!Running)
                    _RainHeadColor 
= value;
                
else
                    
throw new Exception("运行中不可以更改颜色。");
            }

        }



        
private Color _RainBodyColor = Color.Green;
        
/// <summary>
        
/// 获取、设置字符雨体的颜色
        
/// </summary>

        public Color RainBodyColor
        
{
            
get return _RainBodyColor; }
            
set
            
{
                
if (!Running)
                    _RainBodyColor 
= value;
                
else
                    
throw new Exception("运行中不可以更改颜色。");
            }

        }


        
private Color _RainTailColor = Color.DarkGreen;
        
/// <summary>
        
/// 获取、设置字符雨体的颜色
        
/// </summary>

        public Color RainTailColor
        
{
            
get return _RainTailColor; }
            
set
            
{
                
if (!Running)
                    _RainTailColor 
= value;
                
else
                    
throw new Exception("运行中不可以更改颜色。");
            }

        }



        
private Font _RainFont = new Font(new FontFamily("Consolas"), 12);
        
/// <summary>
        
/// 获取、设置字符雨的字体样式
        
/// </summary>

        public Font RainFont
        
{
            
get return _RainFont; }
            
set
            
{
                
if (!Running)
                    _RainFont 
= value;
                
else
                    
throw new Exception("运行中不可以更改字体。");
            }

        }


        
private int _StreamsCount = 100;
        
/// <summary>
        
/// 获取、设置字符雨的数量
        
/// </summary>

        public int StreamsCount
        
{
            
get return _StreamsCount; }
            
set
            
{
                
if (!Running)
                    _StreamsCount 
= value;
                
else
                    
throw new Exception("运行中不可以更改数量。");
            }

        }


        
private int _MaxLength = 50;
        
/// <summary>
        
/// 获取、设置每个雨滴的最大字符数
        
/// </summary>

        public int MaxLength
        
{
            
get return _MaxLength; }
            
set
            
{
                
if (!Running)
                
{
                    
if (value >= _MinLength)
                        _MaxLength 
= value;
                    
else
                        
throw new Exception("最大字符数不能小于最小字符数。");
                }

                
else
                    
throw new Exception("运行中不可以更改数量。");
            }

        }


        
private int _MinLength = 20;
        
/// <summary>
        
/// 获取、设置每个雨滴的最小字符数
        
/// </summary>

        public int MinLength
        
{
            
get return _MinLength; }
            
set
            
{
                
if (!Running)
                
{
                    
if (value <= _MaxLength)
                        _MinLength 
= value;
                    
else
                        
throw new Exception("最小字符数不能大于最大字符数。");
                }

                
else
                    
throw new Exception("运行中不可以更改数量。");
            }

        }


        
public bool Running
        
{
            
get return RainThread != null && RainThread.IsAlive; }
        }


        
private Thread RainThread;
        
private bool RUN;
        
private bool PAUSE;
        
private Brush BackBrush = null;

        
private Brush RainHeadBrush = null;
        
private Brush RainBodyBrush = null;
        
private Brush RainTailBrush = null;

        
/// <summary>
        
/// 开始显示字符雨
        
/// </summary>

        public void Start()
        
{
            
if (_ShowWindow == null)
                
throw new Exception("没有用于显示的窗口(或控件)");

            
if (!Running)
            
{
                RainThread 
= new Thread(new ParameterizedThreadStart(RainProcess));
                RUN 
= true;
                PAUSE 
= false;
                Ready();
                RainThread.Start();
            }

        }


        
private void Ready()
        
{
            Rain.RainFont 
= _RainFont;
            Rain.RainHeadColor 
= _RainHeadColor;
            Rain.RainBodyColor 
= _RainBodyColor;
            Rain.RainTailColor 
= _RainTailColor;
            Rain.BackColor 
= _ShowWindow.BackColor;

            
if (RainHeadBrush != null)
                RainHeadBrush.Dispose();
            
if (RainBodyBrush != null)
                RainBodyBrush.Dispose();
            
if (RainTailBrush != null)
                RainTailBrush.Dispose();
            
if (BackBrush != null)
                BackBrush.Dispose();

            RainHeadBrush 
= new SolidBrush(Rain.RainHeadColor);
            RainBodyBrush 
= new SolidBrush(Rain.RainBodyColor);
            RainTailBrush 
= new SolidBrush(Rain.RainTailColor);
            BackBrush 
= new SolidBrush(Rain.BackColor);

            
//RainItems.Clear();

            
using (Graphics g = _ShowWindow.CreateGraphics())
            
{
                g.Clear(Rain.BackColor);
            }

        }


        
private EventHandler DrawRainEvent;

        
void CharacterRainPanel_DrawRainEvent(object sender, EventArgs e)
        
{
            
//foreach (Rain.Item item in e.Items)
            
//{
            
//    _ShowWindow.Invalidate(new Rectangle(item.X, item.Y, item.Width, item.Height));
            
//    _ShowWindow.Update();
            
//}

            
using (Graphics g = _ShowWindow.CreateGraphics())
            
{
                g.DrawImage(BMP, 
00);
            }

            
//_ShowWindow.Invalidate(true);
            
//_ShowWindow.Refresh();
        }


        
//private List<Rain.Item> RainItems = new List<Rain.Item>();
        private object RainLOCK = new object();

        
private void RainProcess(object Param)
        
{
            
using (BMP = new Bitmap(_ShowWindow.Width, _ShowWindow.Height))
            
{
                
using (Graph = Graphics.FromImage(BMP))
                
{

                    List
<Rain> Rains = new List<Rain>();
                    
for (int i = 0; i < _StreamsCount; i++)
                        Rains.Add(
new Rain(_MinLength + Rand.Next(_MaxLength - _MinLength)));

                    
while (RUN)
                    
{
                        
if (PAUSE)
                        
{
                            Thread.Sleep(
300);
                            
continue;
                        }


                        DoRain(Rains);

                        Thread.Sleep(
50);
                    }


                    
foreach (Rain r in Rains)
                    
{
                        r.Uninit();
                    }

                }

            }

        }


        
private void DoRain(List<Rain> Rains)
        
{

            
for (int i = 0; i < Rains.Count; i++)
            
{
                Rain rain 
= Rains[i];

                
if (!rain.Inited)
                
{
                    
if (!rain.Init(_ShowWindow.Width, Rand.Next(_ShowWindow.Height / 3)))
                        
continue;
                }


                Rain.Item item 
= rain.GetItem(CHARACTERS[Rand.Next(CHARACTERS.Length)]);
                Rain.Item prior 
= null;

                
if (item == null)
                    
continue;
                
else
                
{
                    
lock (BMPLock)
                    
{
                        
if (item.Y <= BMP.Height)
                        
{
                            
if (item.Type == Rain.Item.RainItemType.Head && rain.Prior.Type != Rain.Item.RainItemType.Tail)
                            
{
                                rain.Prior.Type 
= Rain.Item.RainItemType.Body;
                                prior 
= rain.Prior;
                            }


                            
if (item.Type == Rain.Item.RainItemType.Back)
                            
{
                                prior 
= rain.Prior;
                            }


                            
switch (item.Type)
                            
{
                                
case Rain.Item.RainItemType.Head:
                                    
{
                                        Graph.DrawString(
new string(item.CH, 1), _RainFont, RainHeadBrush, item.X, item.Y);
                                        
break;
                                    }

                                
case Rain.Item.RainItemType.Body:
                                    
{
                                        Graph.DrawString(
new string(item.CH, 1), _RainFont, RainBodyBrush, item.X, item.Y);
                                        
break;
                                    }

                                
case Rain.Item.RainItemType.Tail:
                                    
{
                                        Graph.DrawString(
new string(item.CH, 1), _RainFont, RainTailBrush, item.X, item.Y);
                                        
break;
                                    }

                                
case Rain.Item.RainItemType.Back:
                                    
{
                                        Graph.FillRectangle(BackBrush, item.X, item.Y, item.Width, item.Height);
                                        
break;
                                    }

                            }


                            
if (prior != null)
                            
{
                                
switch (prior.Type)
                                
{
                                    
case Rain.Item.RainItemType.Head:
                                        
{
                                            Graph.DrawString(
new string(prior.CH, 1), _RainFont, RainHeadBrush, prior.X, prior.Y);
                                            
break;
                                        }

                                    
case Rain.Item.RainItemType.Body:
                                        
{
                                            Graph.DrawString(
new string(prior.CH, 1), _RainFont, RainBodyBrush, prior.X, prior.Y);
                                            
break;
                                        }

                                    
case Rain.Item.RainItemType.Tail:
                                        
{
                                            Graph.DrawString(
new string(prior.CH, 1), _RainFont, RainTailBrush, prior.X, prior.Y);
                                            
break;
                                        }

                                    
case Rain.Item.RainItemType.Back:
                                        
{
                                            Graph.FillRectangle(BackBrush, prior.X, prior.Y, prior.Width, prior.Height);
                                            
break;
                                        }

                                }

                            }

                        }

                        
else
                        
{
                            item.Owner.Way 
= Rain.RainWay.Reverse;
                        }

                    }

                }

            }


            
try
            
{
                
if (!_ShowWindow.Disposing)
                    _ShowWindow.Invoke(DrawRainEvent, 
thisnew EventArgs());
            }

            
catch
            
{
            }

        }


        
/// <summary>
        
/// 停止显示字符雨
        
/// </summary>

        public void Stop()
        
{
            RUN 
= false;
        }


        
/// <summary>
        
/// 暂停显示
        
/// </summary>

        public void Pause()
        
{
            PAUSE 
= true;
        }


        
/// <summary>
        
/// 恢复暂停
        
/// </summary>

        public void Resume()
        
{
            PAUSE 
= false;
        }


        
//protected void Paint()
        
//{
        
//    lock (RainLOCK)
        
//    {
        
//        for (int i = RainItems.Count - 1; i >= 0; i--)
        
//        {
        
//            Rain.Item item = RainItems[i];

        
//            if (item.X < BMP.Width)
        
//            {
        
//                if (item.Y < BMP.Height)
        
//                {
        
//                    //if (e.ClipRectangle.X <= item.X && e.ClipRectangle.Y <= item.Y && e.ClipRectangle.Width >= item.Width && e.ClipRectangle.Height >= item.Height)
        
//                    {
        
//                        switch (item.Type)
        
//                        {
        
//                            case Rain.Item.RainItemType.Head:
        
//                                {
        
//                                    e.Graphics.DrawString(new string(item.CH, 1), _RainFont, RainHeadBrush, item.X, item.Y);
        
//                                    break;
        
//                                }
        
//                            case Rain.Item.RainItemType.Body:
        
//                                {
        
//                                    e.Graphics.DrawString(new string(item.CH, 1), _RainFont, RainBodyBrush, item.X, item.Y);
        
//                                    break;
        
//                                }
        
//                            case Rain.Item.RainItemType.Tail:
        
//                                {
        
//                                    e.Graphics.DrawString(new string(item.CH, 1), _RainFont, RainTailBrush, item.X, item.Y);
        
//                                    break;
        
//                                }
        
//                            case Rain.Item.RainItemType.Back:
        
//                                {
        
//                                    e.Graphics.FillRectangle(BackBrush, item.X, item.Y, item.Width, item.Height);
        
//                                    break;
        
//                                }
        
//                        }
        
//                    }
        
//                }
        
//                else
        
//                {
        
//                    item.Owner.Way = Rain.RainWay.Reverse;
        
//                }
        
//            }

        
//            if (item.Type == Rain.Item.RainItemType.Back)
        
//            {
        
//                RainItems.Remove(item);
        
//            }
        
//        }
        
//    }
        
//}

        
private class Rain
        
{
            
private static Font _RainFont;
            
public static Font RainFont
            
{
                
get return _RainFont; }
                
set
                
{
                    _RainFont 
= value;

                    _RainHeight 
= (int)(value.Size * value.FontFamily.GetLineSpacing(FontStyle.Regular) / value.FontFamily.GetEmHeight(FontStyle.Regular)) + 1;
                }

            }


            
public static Color BackColor;

            
public static Color RainBodyColor;

            
public static Color RainHeadColor;

            
public static Color RainTailColor;

            
private static int _RainHeight;
            
public static int RainHeight
            
{
                
get return _RainHeight; }
            }


            
private static object RangeLock = new object();
            
private static List<Rectangle> Ranges = new List<Rectangle>();

            Queue
<Item> Chars = new Queue<Item>();
            
private int _Length;
            
private int _BeginX;
            
private int _BeginY;

            
private RainWay _Way;

            
public RainWay Way
            
{
                
get return _Way; }
                
set { _Way = value; }
            }


            
private Rectangle Range;

            
public enum RainWay : byte
            
{
                
/// <summary>
                
/// 正向
                
/// </summary>

                Obverse = 0,

                
/// <summary>
                
/// 反向
                
/// </summary>

                Reverse = 1
            }


            
public bool Inited
            
{
                
get return Chars.Count > 0; }
            }


            
public bool Init(int Width, int Y)
            
{
                
bool Ret = true;

                Uninit();

                
if (Rand.Next(30< 10)
                    
return false;

                _BeginX 
= Rand.Next(Width);

                Size s 
= TextRenderer.MeasureText(" ", _RainFont);

                Range 
= new Rectangle(_BeginX, Y, s.Width, s.Height * _Length);

                
foreach (Rectangle r in Ranges)
                
{
                    
if (r.Contains(this.Range) || r.IntersectsWith(this.Range))
                    
{
                        Ret 
= false;
                        
break;
                    }

                }


                
if (Ret)
                    Ranges.Add(
this.Range);

                _BeginY 
= Y;
                _Way 
= RainWay.Obverse;
                _Prior 
= null;

                
return Ret;
            }


            
public void Uninit()
            
{
                
if (this.Range != null && Ranges.Contains(this.Range))
                    Ranges.Remove(
this.Range);
            }


            
item

            
public Rain(int Length)
            
{
                _Length 
= Length;
            }


            
public Item GetItem(char CH)
            
{
                Item Ret 
= null;
                
if (_Way == RainWay.Obverse)
                
{
                    Ret 
= Add(CH);
                    
if (Ret == null)
                        _Way 
= RainWay.Reverse;
                }

                
else
                
{
                    Ret 
= Remove();
                    
if (Ret == null)
                        _Way 
= RainWay.Obverse;
                }

                
return Ret;
            }


            
private Item _Prior = null;
            
public Item Prior
            
{
                
get
                
{
                    
return _Prior;
                }

            }


            
private Item Add(char CH)
            
{
                Item Ret 
= null;
                
if (Chars.Count < _Length)
                
{
                    Ret 
= new Item(this, CH, _BeginX, _BeginY + RainHeight * Chars.Count, Chars.Count == 0 ? Item.RainItemType.Tail : Item.RainItemType.Head);
                    
if (Ret.Type == Item.RainItemType.Head)
                        _Prior 
= Chars.Last();
                    Chars.Enqueue(Ret);
                }


                
return Ret;
            }


            
private Item Remove()
            
{
                Item Ret 
= null;
                
if (Chars.Count > 0)
                
{
                    Ret 
= Chars.Dequeue();
                    Ret.Type 
= Item.RainItemType.Back;

                    
if (Chars.Count > 0)
                    
{
                        _Prior 
= Chars.First();
                        _Prior.Type 
= Item.RainItemType.Tail;
                    }

                    
else
                        _Prior 
= null;
                }

                
return Ret;
            }

        }

    }

 

 

 

测试程序下载 

使用的时候,先设定ShowWindow,这个属性决定在哪个控件上显示字符雨,然后还可以设置如下属性:

MaxLength:每条字符雨的最大字符数量

MinLength:每条字符雨的最小字符数量

RainBodyColor:字符雨中间的颜色

RainCharacters:字符雨可以使用到的字符

FainFont:字体

RainHeadColor:最前面的字符的颜色

RainTailColor:最后面字符的颜色

StreamsCount:字符雨的数量

 

还有如下方法可供调用:

Start:开始显示

Stop:停止显示

Pause:暂停显示

Resume:恢复暂停