God Jul 2023

God jul!

Vi vill önska alla prenumeranter och läsare en riktigt god jul! Vi vill också förmedla lite julkänsla med lite programmering som passar högtiden.

Följ processen i Youtube-klippet nedan eller ladda hem koden. Vi har denna gång även gjort en enklare installer för dem som kanske vill provköra direkt.

Xmas.cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using SharpDX.Direct2D1.Effects;
using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using Drawing = System.Drawing;
using Forms = System.Windows.Forms;

namespace Xmas2023
{
    public class Xmas : Game
    {
        private GraphicsDeviceManager _graphics;
        private SpriteBatch _spriteBatch;

        Texture2D _background;
        Texture2D _textureXmas;
        Texture2D _textureWhite;
        byte[] _rawTexture;

        int _screenWidth;
        int _screenHeight;
        bool wasFocused;

        int _scale = 4;

        List<FallingPixel> _fallingPixels = new List<FallingPixel>();
        List<FallingPixel> _pixels = new List<FallingPixel>();
        Random _random = new Random();
        float[] _baseLine;

        Song song;

        public Xmas()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            IsMouseVisible = true;

            if(GraphicsDevice == null)
            {
                _graphics.ApplyChanges();
            }

            _graphics.IsFullScreen = true;
            _graphics.PreferredBackBufferWidth = GraphicsDevice.Adapter.CurrentDisplayMode.Width;
            _graphics.PreferredBackBufferHeight = GraphicsDevice.Adapter.CurrentDisplayMode.Height;
            _graphics.ApplyChanges();
        }

        protected override void Initialize()
        {
            Drawing.Bitmap bmp = new Drawing.Bitmap(Forms.Screen.PrimaryScreen.Bounds.Width,
                Forms.Screen.PrimaryScreen.Bounds.Height);

            using (Drawing.Graphics g = Drawing.Graphics.FromImage(bmp))
            {
                g.CopyFromScreen(0, 0, 0, 0, Forms.Screen.PrimaryScreen.Bounds.Size);
            }

            _screenWidth = GraphicsDevice.PresentationParameters.BackBufferWidth;
            _screenHeight = GraphicsDevice.PresentationParameters.BackBufferHeight;

            _background = new Texture2D(GraphicsDevice, _screenWidth, _screenHeight);
            _textureWhite = new Texture2D(GraphicsDevice, 1, 1);
            int white = int.MaxValue;
            _textureWhite.SetData<int>(new int[] { white });
            _baseLine = new float[_screenWidth / _scale];

            GetTexture2DFromBitmap(bmp);

            for(int x = 0; x<_screenWidth / _scale; x++) 
            {
                for(int y = 0; y<_screenHeight / _scale; y++) 
                {
                    _pixels.Add(new FallingPixel(x * _scale, y * _scale, _random.Next(10)));
                }
            }

            base.Initialize();
        }

        public void GetTexture2DFromBitmap(Drawing.Bitmap bitmap)
        {
            BitmapData data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), 
                System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat);

            unsafe
            {
                uint* byteData = (uint*)data.Scan0;

                // Switch bgra -> rgba
                for (int i = 0; i < data.Height * data.Width; i++)
                {
                    byteData[i] = (byteData[i] & 0x000000ff) << 16 | (byteData[i] & 0x0000FF00) | (byteData[i] & 0x00FF0000) >> 16 | (byteData[i] & 0xFF000000);
                }
            }

            int bufferSize = data.Height * data.Stride;
            _rawTexture = new byte[bufferSize];
            Marshal.Copy(data.Scan0, _rawTexture, 0, _rawTexture.Length);
            _background.SetData(_rawTexture);

            bitmap.UnlockBits(data);
        }

        void checkAppRegainedFocus()
        {
            if (!this.IsActive)
            {
                wasFocused = false;
            }
            else if (!wasFocused && _graphics.IsFullScreen)
            {
                wasFocused = true;
                _graphics.ToggleFullScreen();
                _graphics.ToggleFullScreen();
            }
        }

        void SetPixel(int x, int y, Color color)
        {
            for (int xx = x; xx < x + _scale && xx < _screenWidth; xx++)
            {
                for (int yy = y; yy < y + _scale && yy < _screenHeight; yy++)
                {
                    int index = xx * 4 + yy * 4 * _screenWidth;
                    _rawTexture[index] = color.R;
                    _rawTexture[index + 1] = color.G;
                    _rawTexture[index + 2] = color.B;
                    _rawTexture[index + 3] = color.A;
                }
            }
        }

        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(GraphicsDevice);
            _textureXmas = Content.Load<Texture2D>("godjul");

            song = Content.Load<Song>("WinterWonderland");
            MediaPlayer.IsRepeating = true;
            MediaPlayer.Play(song);
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            if(_pixels.Count > 0 && _fallingPixels.Count < 800)
            {
                int pIndex = _random.Next(0, _pixels.Count);
                _fallingPixels.Add(_pixels[pIndex]);
                SetPixel(_pixels[pIndex].X, _pixels[pIndex].Y, Color.Transparent);
                _pixels.RemoveAt(pIndex);
            }

            foreach(var item in _fallingPixels) 
            {
                item.Update();

                if(item.Position.Y >= (_screenHeight - _baseLine[item.X / _scale] * _scale))
                {
                    _baseLine[item.X / _scale] += 0.1f;
                    SetPixel(item.X, (int)(_screenHeight - _baseLine[item.X / _scale] * _scale), Color.White);
                }
            }

            _fallingPixels.RemoveAll(p => p.Position.Y > _screenHeight);

            _background.SetData(_rawTexture);
            checkAppRegainedFocus();

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            _spriteBatch.Begin();

            _spriteBatch.Draw(_textureXmas, new Vector2(_screenWidth / 2 - _textureXmas.Width / 2,
                _screenHeight / 2 - _textureXmas.Height / 2), Color.White);
            _spriteBatch.Draw(_background, Vector2.Zero, Color.White);

            _spriteBatch.Draw(_textureXmas, new Vector2(_screenWidth / 2 - _textureXmas.Width / 2,
               _screenHeight / 2 - _textureXmas.Height / 2), new Color(0.3f, 0.3f, 0.3f, 0.1f));

            foreach (var pixel in _fallingPixels)
            {
                _spriteBatch.Draw(_textureWhite, new Rectangle((int) pixel.Position.X, (int)pixel.Position.Y, _scale, _scale),
                    Color.White);
            }

            _spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}
FallingPixel.cs
using Microsoft.Xna.Framework;

namespace Xmas2023
{
    class FallingPixel
    {
        public int X { get; set; }
        public int Y { get; set; }
        public Vector2 Position { get; set; }
        public Vector2 Velocity { get; set; }

        public FallingPixel(int x, int y, int speed)
        {
            X = x;
            Y = y;
            Position = new Vector2(x, y);
            Velocity = new Vector2(0, 1 - speed / 20f);
        }

        public void Update()
        {
            Position += Velocity;

        }
    }
}
Scroll to top